Hello everyone, I am xiao CAI, a desire to do CAI Not CAI xiao CAI in the Internet industry. Soft but just, soft praise, white piao just! Ghost ~ remember to give me a three – even oh!

This article focuses on the use of polymorphism in Java

Refer to it if necessary

If it is helpful, do not forget the Sunday

Wechat public number has been opened, xiao CAI Liang, not concerned about small partners remember to pay attention to oh!

Today is Friday, as usual to check out the company. Sit down at your desk and turn on the computer. “Another brick day.” After thinking about it, I still “skillfully” opened Idea, looked at today’s requirements, and then knocked up the code. Why, who wrote these codes, how to appear in my code, and is still waiting to submit the state, I remember I did not write ah, with interest to see:

This is not polymorphic, who wrote the test in my computer, can not help but a while strange.

"Do you see what this produces?"

I heard a voice behind me, and as I was thinking about the output and not paying attention to the source of the sound, I continued to look at the code and concluded:

    polygon() before cal()
    square.cal(), border = 2
    polygon() after cal()
    square.square(), border = 4
Copy the code

I thought to myself: Is that it? At least is also a Java development engineer, although usually moving bricks, some basic skills or some. Can not help but a little proud ~

"Is that your answer? Looks like you're no good either."

Voice suddenly sounded again, this time I don’t calm down, fuck! I thought of that answer in my mind, too, who can see it, and the words make people want to do a set of Wei 18.” Who are you?” He turned his head with a hint of confusion and anger. Why no one? I don’t have to wonder, “Vegetables, wake up, how did you fall asleep on the job?”

Asleep at work? I opened my eyes, looked at the surrounding environment, it was a dream ah, relieved. Look to see the department head standing in front of me, sleeping during work time, you are not feeling well or what? Yesterday, I wrote a bunch of bugs and did not change them. Today, I submitted some messy things. I think your performance this month is not what you want.

“I’m not, I’m not, I don’t know how I fell asleep, you listen to me!” Haven’t had time to speak this sentence, the in the mind of the flowers I want to take you home, in the late night bar which tube it is true or not, please forget rocking like him, you are the most attractive, you know, the alarm clock rang, I immediately stand up and back slightly damp, the forehead top micro sweat, looked at the mobile phone, on Saturday, 8 a.m., the original is a dream!

Strange. Why do you have such strange dreams? It’s so scary. Then I thought of that part of the code in the dream, is my result wrong? With memory, I re-typed it on the computer, and the running results are as follows:

/*
    polygon() before cal()
    square.cal(), border = 0
    polygon() after cal()
    square.square(), border = 4
*/
Copy the code

Square. CAL (), border = 0 instead of 2. Am I not even polymorphic now? Computer phone in front of you, do not know whether to get the right answer! Whether or not, let’s review polymorphisms with a side dish.

Square. CAL (), border = 4; So let’s play it off with a doubt!

polymorphism

In object-oriented programming languages, polymorphism is the third basic feature after data abstraction and inheritance.

Polymorphism not only improves the organization and readability of code, but also creates extensible programs. The function of polymorphism is to decouple types.

1. Transition upward

According to the Richter’s substitution principle: wherever a base class can appear, a subclass must appear.

An object can be used either as its own type or as its base type. The practice of treating a reference to an object as a reference to its base type is called an upcast. Because the parent class is above the child class, the child class refers to the parent class and is called upcast.

public class Animal {
    void eat(a) {
        System.out.println("Animal eat()"); }}class Monkey extends Animal {

    void eat(a) {
        System.out.println(" Monkey eat()"); }}class test {

    public static void start(Animal animal) {
        animal.eat();
    }

    public static void main(String[] args) {
        Monkey monkey = newMonkey(); start(monkey); }}/* OUTPUT:
Monkey eat()
*/
Copy the code

The start() method in the test class above accepts a reference to Animal and can also accept an exported class from Animal. When you call the eat() method, you automatically use the eat() method defined in the Monkey without doing any type casting. Because going from Monkey up to Animal only reduces the number of interfaces, not the number of interfaces that Animal has.

The analogy is not particularly apt: your father’s property will be inherited to you, but yours will remain yours, and you will have no less on balance than your father’s.

Forgetting object types

In the test.start() method, the definition passes in a reference to Animal, but passes in a reference to Monkey. This seems to forget the Monkey object type, so why not just define the test method as void start(Monkey Monkey), Wouldn’t that make a little bit more sense.

The intuition may be an advantage, but it brings up other problems: Animal has more than one Monkey derived class, so we need to define a void start(Monkey Monkey) method for Animal. Laziness is the nature of developers.

So you have polymorphism

2. Show your strengths

Method calls are divided into static binding and dynamic binding. What’s binding: Associating a method call with a method body is called binding.

  • Static binding: also known as theEarly binding. The binding is performed before the program is executed. When we hear the word “static”, we think of itstaticKeywords, bystaticVariables decorated with keywords become static variables that are initialized before the program executes.Early bindingIs the default binding method in procedural languages, such as C, which has only one method call, pre-binding.

Lead to thinking:

public static void start(Animal animal) {
    animal.eat();
}
Copy the code

If there are more than one Animal exported class, how do you know which method to call when you execute eat()? This can’t be done by pre-binding. Hence the late binding.

  • Dynamic binding: also known as theLate binding. Is bound according to the object type while the program is running, so it can also be calledRuntime binding. Java, on the other hand, relies on its own late binding mechanism so that at run time it can determine the type of the object and call the correct method.

Summary:

In Java, all except static and final modified methods are late bound

Reasonable is right

Obviously it makes sense to implement polymorphism through dynamic binding. In this way, we only need to pass in references to the base class when developing the interface, so that the code will run correctly for all the base exported classes.

Monkey, Pig and Dog are all derived from Animal

Animal Animal = new Monkey(); Animal Animal = new Monkey(); Animal Animal = new Monkey() But you end up calling the Monkey’s own eat() method instead.

Animal is a base class that creates a common interface for exporting classes. All derived classes that inherit from Animal can have their own unique implementation behavior.

scalability

With polymorphism, we can add as many new types to the system as we want without overloading the void Start (Animal Animal) method.

In a well-designed OOP program, where most or all of the methods follow the start() model and only follow the base class interface, the program is extensible and we can add functionality by inheriting new data types from common base classes. Methods that manipulate the base class interface can be applied to the new class without any changes.

The failure?

Let’s review permission modifiers:

scope The current class With a package Children of class The other package
public Square root Square root Square root Square root
protected Square root Square root Square root x
default Square root Square root x x
private Square root x x x
  • Public: All classes are visible
  • Protected: This class, this package, and subclasses are visible
  • Default: This class and this package are visible
  • Private: This class is visible

Failures caused by private methods:

Let’s look at another set of code:

public class PrivateScope {

    private void f(a) {
        System.out.println("PrivateScope f()");
    }

    public static void main(String[] args) {
        PrivateScope p = newPrivateOverride(); p.f(); }}class PrivateOverride extends PrivateScope {

    private void f(a) {
        System.out.println("PrivateOverride f()"); }}/* OUTPUT PrivateScope f() */
Copy the code

Is it a little strange that f() is called in the base class instead of the f() defined in the export class PrivateOverride, as described above, by dynamic binding? If you’ve noticed, the f() method modifier in the base class is private. Yes, that’s the problem. The f() method defined in private Override is a brand new method that is invisible to subclasses because of private and cannot be overridden.

Conclusion:

Only methods that are not private can be overridden

When we write code with Idea, we can use @override on the head of the overridden method. If the method is not overridden, the annotation @override will return an error:

This is also a good indication that the method is not overwritten, but is completely new.

Domain failures:

When you see this, you start to think that everything (except the private modifier) can happen polymorphically. In reality, however, only ordinary method calls can be polymorphic. This is where polymorphism is wrong.

Let’s look at the following code:

class Super {
    public int field = 0;

    public int getField(a) {
        returnfield; }}class Son extends Super {
    public int field = 1;

    public int getField(a) {
        return field;
    }

    public int getSuperField(a) {
        return super.field; }}class FieldTest {
    public static void main(String[] args) {
        Super sup = new Son();
        System.out.println("sup.field:" + sup.field + " sup.getField():" + sup.getField());

        Son son = new Son();
        System.out.println("son.field:" + son.field + " son.getField:" + son.getField() + " son.getSupField:"+ son.getSuperField()); }}/* OUTPUT
sup.field:0 sup.getField():1
son.field:1 son.getField:1 son.getSupField:0
*/
Copy the code

From the code above we can see that the output value of sup.field is not defined in the Son object, but in Super itself. This is a little bit of a conflict with what we know about polymorphism.

This is not true; when a Super object is converted to a Son reference, any domain access operations will be resolved by the compiler and therefore not polymorphic. In this case, super. field and son. field are allocated different storage Spaces, and the Son class is exported from the Super class, so Son actually contains two fields called field: its own +Super.

While this may seem like a headache, our development specification typically makes all fields private so that they cannot be accessed directly, only by calling methods.

Static causes failure:

If a method is static, its behavior is not polymorphic. If a method is static, its behavior is not polymorphic.

As usual, let’s look at this set of code:

class StaticSuper {

    public static void staticTest(a) {
        System.out.println("StaticSuper staticTest()"); }}class StaticSon extends StaticSuper{

    public static void staticTest(a) {
        System.out.println("StaticSon staticTest()"); }}class StaticTest {
    public static void main(String[] args) {
        StaticSuper sup = newStaticSon(); sup.staticTest(); }}/* OUTPUT
StaticSuper staticTest()
*/
Copy the code

Static methods are associated with classes, not objects

Constructors and polymorphism

The first thing we need to understand is that constructors are not polymorphic, because constructors are actually static methods, but the static declaration is implicit.

Let’s go back to the mysterious code at the beginning:

The output result is:

/*
    polygon() before cal()
    square.cal(), border = 0
    polygon() after cal()
    square.square(), border = 4
*/
Copy the code

We can see that the first output is the constructor method of the base polygon class.

This is because the constructors of the base class are always called during the construction of the exported class, and are gradually linked up through the inheritance hierarchy so that the constructors of each base class are called.

Because constructors have a special job: checking that objects are constructed correctly. An exported class can access only its own members, not members of the base class (which are usually of private type). Only the constructor of the base class has permission to initialize its own elements. Therefore, all constructors must be called, or the complete object cannot be constructed correctly.

The steps are as follows:

  • Call the base class constructor, and the steps continue recursively, first constructing the root of the hierarchy, then exporting the class at the next level… , down to the bottom exported class
  • Call the initialization methods of members in declared order
  • Call the export class to construct its body

It’s not a particularly appropriate analogy: do you have to have your father before you appear, and do you have to have your grandfather before your father appears, which is the way of gradually linking up

Polymorphic behavior inside a constructor

Ever wonder what would happen if one of the dynamically bound methods of the object being constructed were called inside a constructor? Dynamically bound calls are determined at run time because the object has no way of knowing whether it belongs to the class of the method or a derived class of that class. If you call a dynamically bound method inside a constructor, you use the overridden definition of that method. However, because overridden methods are called before the object is fully constructed, this can lead to hidden errors that are hard to find.

Problem lead:

A dynamically bound method call that extends out into the inheritance hierarchy can call methods in the exported class, and if we do this inside the constructor, we might call a method that manipulates members that have not yet been initialized, which is a sure recipe for disaster.

You might have thought of the code at the beginning:

The output is:

/*
    polygon() before cal()
    square.cal(), border = 0
    polygon() after cal()
    square.square(), border = 4
*/
Copy the code

In the polygon constructor, there is a CAL () method. At this time, the dynamic binding mechanism is adopted and CAL () of square is called. At this time, the border variable is not initialized. Int defaults to 0, so we have square.cal(), border = 0. See here, friends are not a kind of cloud to see the feeling of the sky!

The actual initialization of this set of code is as follows:

  • Initialize the storage allocated to an object to binary zeros before anything else happens
  • When the base class constructor is called, the overridden one is calledcal()Method, so border has a value of 0 because of step 1
  • Call the initialization methods of the members in the declared order
  • Invoke the constructor body of the exported class

Hu ~ finally finished the review of polymorphisms, fortunately it is a dream, no one found my dishes. I don’t know whether you are the same as xiao CAI in front of the computer and mobile phone. If so, I will review with xiao CAI quickly, so as not to let others find that I won’t be polymorphic!

Do not know next time can do what kind of dream ~

Today you work harder, tomorrow you will be able to say less words!

I am xiao CAI, a man who studies with you. 💋

Wechat public number has been opened, xiao CAI Liang, not concerned about small partners remember to pay attention to oh!