Polymorphism is generally divided into two types: overriding polymorphism and overloading polymorphism.

  • Overloading polymorphism, also called compile-time polymorphism. That is to say, this kind of polymorphism is already determined at compile time. Everyone knows overloading: a group of methods with the same method name but different parameter lists is overloading. When calling such overloaded methods, different results are obtained by passing different parameters.

    But there is ambiguity here. Some people think overloading should not be counted as polymorphism. Because many people understand polymorphism as: the specific type pointed to by a reference variable defined in the program and the method call issued through that reference variable are not determined at programming time, but are determined during program runtime. This situation is called polymorphism. What this definition describes is our second type of polymorphism—overriding polymorphism. Moreover, overloading polymorphism is not unique to object-oriented programming, while polymorphism is one of the three major characteristics of object-oriented programming (if I am wrong, remember to tell me...).

    I think there is no need to delve deeply into these definitions. My understanding is: the ability of the same behavior to have multiple different forms or appearances is polymorphism, so I think overloading is also a kind of polymorphism. If you disagree with this view, I accept it.

  • Overriding polymorphism, also called runtime polymorphism. This kind of polymorphism is implemented through dynamic binding technology. It means determining the actual type of the referenced object during execution, and calling its corresponding method according to its actual type. In other words, only when the program runs do you know which subclass's method is called. This kind of polymorphism is implemented through method overriding and upcasting. The example in our code above is a complete overriding polymorphism. All polymorphism we discuss next is overriding polymorphism, because it is the real polymorphism in object-oriented programming.

Upcasting

Converting an object referenced by a subclass reference to a superclass type is called upcasting. In plain terms, it is converting a subclass object into a superclass object. Here the superclass type can be an interface.

Look at a well-known example:

Example

public class Animal { public void eat(){ System.out.println("animal eatting..."); } } public class Cat extends Animal{ public void eat(){ System.out.println("I eat fish"); } } public class Dog extends Animal{ public void eat(){ System.out.println("I eat bones"); } public void run(){ System.out.println("I can run"); } } public class Main { public static void main(String[] args) { Animal animal = new Cat(); //Upcasting animal.eat(); animal = new Dog(); animal.eat(); } } //Result: //I eat fish //I eat bones

This is upcasting.Animal animal = new Cat();It converts the subclass object Cat into the parent class object Animal. At this time, the method called by the animal reference is the subclass method.

Issues to note during casting

  • When upcasting, methods defined separately in the subclass are lost. For example, the run method defined in the Dog class above cannot be accessed when the animal reference points to a Dog class instance.animal.run()An error will be reported.
  • A subclass reference cannot point to a parent class object.Cat c = (Cat)new Animal()This is not allowed.

Benefits of upcasting

  • Reduces duplicate code and makes the code concise.
  • Improves system extensibility.

Downcasting

Corresponding to upcasting is downcasting. Downcasting is converting a parent class object into a subclass object. (Please note! There is a pitfall here.)

Example-driven

Let's look at an example first:

//还是上面的animal和cat dog
Animal a = new Cat();
Cat c = ((Cat) a);
c.eat();
//输出  我吃鱼
Dog d = ((Dog) a);
d.eat();
// 报错 : java.lang.ClassCastException:com.chengfan.animal.Cat cannot be cast to com.chengfan.animal.Dog
Animal a1 = new Animal();
Cat c1 = ((Cat) a1);
c1.eat();
// 报错 : java.lang.ClassCastException:com.chengfan.animal.Animal cannot be cast to com.chengfan.animal.Cat

Why doesn't the first piece of code report an error? By now you probably know: because a itself is a Cat object, it can naturally be downcast to Cat, and naturally cannot be cast to Dog. Have you ever seen such a damn thing as a dog suddenly turning into a cat?

And a1 is an Animal object; it cannot be downcast to any subclass object. For example, if you do archaeological work and discover a new creature, you know it is an animal, but you cannot directly say, ah, it is a cat, or it is a dog.

Notes on downcasting

  • The premise of downcasting is that the parent class reference points to a subclass object (that is, before downcasting, it must first be upcast).
  • Downcasting can only be cast to an object of its own class (a cat cannot become a dog).

Look at a classic case:

Example

class A { public String show(D obj) { return ("A and D"); } public String show(A obj) { return ("A and A"); } } class B extends A{ public String show(B obj){ return ("B and B"); } public String show(A obj){ return ("B and A"); } } class C extends B{ } class D extends B{ } public class Demo { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println("1--" + a1.show(b)); System.out.println("2--" + a1.show(c)); System.out.println("3--" + a1.show(d)); System.out.println("4--" + a2.show(b)); System.out.println("5--" + a2.show(c)); System.out.println("6--" + a2.show(d)); System.out.println("7--" + b.show(b)); System.out.println("8--" + b.show(c)); System.out.println("9--" + b.show(d)); } } //Result: //1--A and A //2--A and A //3--A and D //4--B and A //5--B and A //6--A and D //7--B and B //8--B and B //9--A and D //Can you understand this result? First analyze it yourself.

For the first three, with forced analysis, you can still understand them. But for the fourth, you're probably dumbfounded. Why isn't it "B and B"?

Here you need to learn something new.

When a parent class reference variable references a subclass object, the type of the referenced object determines whose member method is called, and the type of the reference variable determines which methods can be called. If the subclass does not override the method, then it will look for it in the parent class.

It may sound a bit awkward. Let's first look at a simple example:

Example

class X { public void show(Y y){ System.out.println("x and y"); } public void show(){ System.out.println("only x"); } } class Y extends X { public void show(Y y){ System.out.println("y and y"); } public void show(int i){ } } class main{ public static void main(String[] args) { X x = new Y(); x.show(new Y()); x.show(); } } //Result //y and y //only x

Y inherits X, overrides the show(Y y) method in X, but does not override the show() method.

At this time, x, whose reference type is X, points to an object of Y. At this time, the method called is determined by Y, and it will first look in Y. Executex.show(new Y());, this method is defined in Y, so what is executed is the method in Y;

But when executingx.show();, some people might say: doesn't Y not have this method? It seems to go to the parent class to find the method, because the method in X was called.

In fact, class Y does have a show() method. This method is inherited from X, just not overridden, so it is not explicitly written in Y. It looks like the method in X is called, but in fact the one called is still in Y.

Now it should not be difficult to understand the hard-to-understand sentence above. X is the reference variable type, which determines which methods can be called; show() and show(Y y) can be called, while show(int i) cannot be called. Y is the type of the referenced object, which determines whose method is called: Y's methods are called.

The above is a simple piece of knowledge; it is not enough to help us understand that complex example. Let's look at another piece of knowledge:

Priority of method calls on objects in the inheritance chain: this.show(O), super.show(O), this.show((super)O), super.show((super)O).

If you can understand this calling relationship, then you have mastered polymorphism. Let's go back to that complex example:

The relationship of a, b, c, and d is as follows: C/D —> B —> A

Let's first analyze 4:a2.show(b)

  • First, a2 is a reference variable of type A, pointing to an object of type B. A determines the callable methods: show(D obj) and show(A obj).
  • a2.show(b) ==> this.show(b), here this refers to B.
  • Then look for show(B obj) in class B. It is found, but unfortunately useless, because show(B obj) is not within the callable scope.this.show(O)Failed, enter the next level:super.show(O), super refers to A.
  • Look for show(B obj) in A. Failed, because this method is not defined. Enter the third level:this.show((super)O), this refers to B.
  • In B, find show((A)O). Found it: show(A obj), choose to call this method.
  • Output: B and A

If you can understand this process and can analyze the other cases, then you have truly mastered it.

Let's look at 9 again:b.show(d)

  • First, b is a reference variable of type B, pointing to an object of type B. Since no upcasting is involved, only methods in its own class will be called.
  • Look for the method show(D obj) in B. Now you won't say it's not found, right? It is found, and that method is called directly.
  • Output: A and D.

Summary

This article roughly covers these contents. Let's summarize.

  1. Polymorphism, in short, is the ability of the same behavior to have multiple different forms or appearances.
  2. Classification of polymorphism: runtime polymorphism and compile-time polymorphism.
  3. Prerequisites for runtime polymorphism: inheritance (implementation), overriding, and upcasting.
  4. Upcasting and downcasting.
  5. Priority of method calls on objects in the inheritance chain: this.show(O), super.show(O), this.show((super)O), super.show((super)O).

Source article: http://www.cnblogs.com/kexianting/p/8689031.html