Questions about polymorphism in Java

Through the previous study, we know that Java is object-oriented programming, and oriented programming has three major characteristics: encapsulation, inheritance and polymorphism. Encapsulation hides the internal implementation mechanism of the class, and can change the internal structure of the class without affecting its use, while also protecting the data. Only its internal details are hidden from the outside world, and only its access methods are exposed to the outside world. Inheritance is to reuse parent class code. When multiple subclasses inherit parent class members, these members can be used multiple times in subclasses, which solves the problem of program reusability. At the same time, inheritance has also paved the way for polymorphism. So what is polymorphism? What is the implementation mechanism of polymorphism? Let’s reveal them one by one for you:

1. What is polymorphism?

An example: For example, if you are a god of wine and have a special love for wine, you go home one day and find several glasses filled with white wine. From the outside, it is impossible for us to know what kind of wine these are. Only after drinking You can guess what kind of wine it is. When you drink it, it is Jiannanchun, then it is Wuliangye, and then it is Jiuguijiu… Here we can describe it as follows: \

What is shown here is polymorphism. Jiannanchun, Wuliangye, and Jiuguijiu are all subcategories of wine. We can derive different subcategories just through the parent category of wine. This is polymorphism.

Simply put:

We only know the specific instance object pointed to by the reference variable (that is, the specific wine) at run time (that is, when drinking a certain glass of wine). Polymorphism means: the same reference type, using different instances to perform different operations. (Multiple operating states)

If you want to be more specific:

1. The so-called polymorphism refers to the specific type pointed to by the reference variable defined in the program and the method calls issued through the reference variable. They are not determined during programming, but are determined during the running of the program.

2. Which class instance object a reference variable will point to, and which class implements the method call issued by the reference variable, must be determined during the running of the program.

3. Because the specific class is determined when the program is running, the reference variable can be bound to various class implementations without modifying the source program code, causing the specific method called by the reference to change accordingly. , that is, you can change the specific code bound to the program when it is running without modifying the program code, allowing the program to select multiple running states. This is polymorphism.

2. How to achieve polymorphism?

1. First, I create a class called “Master”. He likes to raise pets (Pet). He raises Dog (dog) and Cat (cat) classes. Then what does he have to do every day? Just feed cats or dogs. Now I am going to use a program to describe this thing, as follows:

(1) First create a new module called day_1109, then create a package called com.feisi.demo in its src directory (you can read my blog about the contents of these modules and packages), and then create a class called \ “Pet class”Pet class, and turn it into an abstract class (because pets are features extracted from cats and dogs). Then define an “eat” abstract method inside, because different pets eat different things.

package com.feisi.demo;
//Parent class: abstract class
public abstract class Pet {
    public abstract void eat();
}

(2) Create a “cat” Cat class under the package and let it inherit the “pet class” Pet class, then it must implement the abstract method of its parent class. (If you don’t understand clearly, go to my blog)

package com.feisi.demo;

public class Cat extends Pet{
  //Subclass overrides parent class method
    @Override
    public void eat() {
       System.out.println("Cat eats fish....");
    }
}

(3)) Create a “dog” Dog class under the package, and let it inherit the “pet class” Pet class, and it must also implement the abstract method of its parent class.

package com.feisi.demo;

public class Dog extends Pet{
    @Override
    public void eat() {
        System.out.println("Dogs eat meat....");
    }
}

(4) Create another “Master” class, because he has to feed the pets every day, so he created a method “Feed” feed() method, because he has to tell me which dog to feed. Or which cat, so the value passed in the parameter list should be an object.

package com.feisi.demo;

public class Master {
    public void feed(Dog dog){
        dog.eat();//Feed the dog
    }
    public void feed(Cat cat){
        cat.eat();//Feed the cat
    }
}

At this point I will find that the method names and return values of the two feed() methods are the same, but their parameter lists are different. This is a typical overloading of themethod. At this time, the overall design of the question was designed.

But if this person wants to raise some other pets, such as hamsters, will he have to write another class called “hamster”, which means that our feed() method will have to Add one, passing in a hamster object. In a few days, he will become more interested and want to keep some other pets. Should he add them one by one like before? Then you will find that as time goes by, it means that we need to constantly overload the class, which will be very troublesome.

(5) So we thought that we could write like this: So since cats, dogs, hamsters, etc… they all belong to the “Pet” Pet class, then we can change the feed() method: ( At this time, our method covers a wider range than what we wrote before)

package com.feisi.demo;

public class Master {
    //The method is more scalable
    //Pet: The parameter object is diverse (polymorphism: Dog, Cat, Tiger....)
    
public void feed(Pet pet) {
        //Compile-time polymorphism: Which class the method the object calls belongs to at compile time will call the method of that class.
        pet.eat(); //This is the method called by the parent class, which is also the method called by the parent class Pet.
    }
    /*
    public void feed(Dog dog){
        dog.eat();//Feed the dog
    }
    public void feed(Cat cat){ //Comment out the previously written method
        cat.eat();//Feed the cat
    }
     */
}

Now, no matter how many pets you have, as long as it is a pet, this is the only way to feed it.

(6) Next we write a test class Test, and create a master object, a dog object and a cat object in it. (During inheritance, the parent class cannot call the subclass method, but the subclass can call the parent class. method)

package com.feisi.demo;

public class Test {
    public static void main(String[] args) {
        Master master =new Master();

        Dog dog =new Dog();

        Cat cat =new Cat();
    }
}

Since dogs and cats are both pets, can we write it like this and change it to:

package com.feisi.demo;

public class Test {
    public static void main(String[] args) {
        Master master =new Master();
        //The reference of the parent class points to the subclass object (that is, the reference of the parent class, the instance of the subclass)
        Pet dog =new Dog();
        //The reference of the parent class points to the subclass object (that is, the reference of the parent class, the instance of the subclass)
        Pet cat =new Cat();
        master.feed(dog);
        master.feed(cat);
    }
}

(7) The result of running the code is: (The result is very surprising, why does this achieve our actual needs)

Although the parameters in our feed() method,are methods called by the Pet class object when compiled, but in fact, each subclass object calls this method at runtime . So why?

(8) Let me take one of the examples here: Pet dog = new Dog(); (Use a class operation diagram to explain)

At this time, pay attention to a problem: Although we do know that the parameter is of Pet type at compile time, we are the method called by the Pet class object. Compile time polymorphism: When the program is compiled, which class the object belongs to will be called. class methods. But when we actually run it, it needs to find the memory first and find the actual object type corresponding to a class called Dog. Then its prototype points to the eat() method of the Dog class. The same reason , the same goes for cats. This is runtime polymorphism: when the program is running, the actual type of the corresponding object is found in the heap based on the reference of the parent class, and then the method of that type is called.

3. Why use polymorphism?

1. Program scalability issues.

4. Summary How to achieve polymorphism?

1. Multiple subclasses override the methods of the parent class.

2. The reference of the parent class points to the object of the subclass:

Here you can simplify the previous code when passing parameters to the feed() method:

Then the parameters in the feed() method in the Master class become Pet pet =new Dog() or Pet pet =new Cat (), which is actually the same thing.

5. Finally, I leave one exercise question for you to consolidate your knowledge, haha! (A picture is attached below. I will publish my reference answer in the next issue. Of course, that is my approach. You must have more novel ideas)