Type conversion of Java polymorphic objects <br />The object type conversion mentioned here refers to an object with an inheritance relationship, not an object of any type. When casting an object that does not have an inheritance relationship, the java runtime throws a java.lang.ClassCastException exception.
In the inheritance chain, we call the subclass conversion to the parent class "upward transformation" and the parent class conversion to the child class "downward transformation".
Many times, we define variables as the type of the parent class but refer to the object of the child class. This process is upward transformation. When the program runs, it uses dynamic binding to realize the call to subclass methods, that is, polymorphism.
However, sometimes in order to complete the functions that some parent classes do not have, we need to convert the upward-transformed subclass objects into subclasses and call the subclass methods, which is downward transformation.
Note: The object of the parent class cannot be directly cast to a subclass type, and the subclass object after upward transformation can only be converted to a subclass type again. In other words, subclass objects must be transformed upward before they can transform downward. Please see the following code:
public class Demo { public static void main(String args[]) { SuperClass superObj = new SuperClass(); SonClass sonObj = new SonClass(); // The following code will be run when it is Throw an exception and cannot convert the parent class object directly is a subclass type // SonClass sonObj2 = (SonClass)superObj; // Transform up first, then transform down superObj = sonObj; SonClass sonObj1 = (SonClass)superObj; }}class SuperClass{ }class SonClass extends SuperClass{ }Remove the comment on line 7, and an exception will be thrown at runtime, but the compilation can be passed.
Because there are risks in transitioning downwards, when receiving a reference to the parent class, be sure to use the instanceof operator to determine whether the object is the subclass you want. Please see the following code:
public class Demo { public static void main(String args[]) { SuperClass superObj = new SuperClass(); SonClass sonObj = new SonClass(); // superObj is not S instance of onClass class if(superObj instanceof SonClass){ SonClass sonObj1 = ( SonClass)superObj; }else{ System.out.println("① Cannot convert"); } superObj = sonObj; // superObj is an instance of the SonClass class if(superObj instanceof SonClass){ SonCl ass sonObj2 = (SonClass)superObj; }else { System.out.println("② cannot be converted"); } }}class SuperClass{ }class SonClass extends SuperClass{ } Running results:
① Cannot convert
Summary: The type conversion of an object is checked when the program is run. Upward transformation will be automatically performed. The downward transformation object must be a subclass of the current reference type.
Java polymorphism and dynamic binding <br />In Java, the variables of the parent class can refer to instances of the parent class or instances of the child class.
Please read a piece of code first:
public class Demo { public static void main(String[] args){ Animal obj = new Animal(); obj.cry(); obj = new Cat(); obj.cry(); obj = new Dog() ; obj .cry(); }}class Animal{ // The call of animals public void cry(){ System.out.println("I don't know what to call"); } }class Cat extends Animal{ // The call of cat public void cry(){ System.out.println("Meow~"); }}class Dog extends Animal{ // Dog barking public void cry(){ System.out.println("Wow~"); } } Running results:
I don't know how to call meow~Wool~
The above code defines three classes, namely Animal, Cat and Dog. Cat and Dog classes are all inherited from Animal classes. The obj variable has type Animal, which can point to instances of Animal class, as well as instances of Cat and Dog classes, which is correct. That is, the variables of the parent class can refer to instances of the parent class or instances of the child class. Note that the other way around is wrong, because all cats are animals, but not all animals are cats.
It can be seen that obj can be either a human, a cat or a dog. It has different forms of expression, which is called polymorphism. Polymorphism refers to a thing having different forms or forms.
For example, "humans" also have many different expressions or implementations. They can be drivers, teachers, doctors, etc. When you hate yourself, you will say "become a new person in the next life", so you can become a driver, teacher, or doctor in the next life. We It is said that "human" has polymorphism.
There are three necessary conditions for polymorphism to exist: inheritance, rewrite, and parent variables refer to subclass objects.
When calling a method using a polymorphic method:
First check whether the method is present in the parent class. If not, a compilation error is made; if there is, check whether the subclass overrides the method.
If the subclass overrides the method, the subclass's method is called, otherwise the parent class method is called.
As can be seen from the above example, one advantage of polymorphism is that when there are many subclasses, there is no need to define multiple variables. You can only define a variable of the parent class type to refer to instances of different subclasses. Please look at the following example:
public class Demo { public static void main(String[] args){ // With the help of polymorphism, the owner can feed many animals Master ma = new Master(); ma.feed(new Animal(), new Food()); ma.feed(new Cat(), new Fish()); ma.feed(new Dog(), new Bone()); }}// Animal class and its subclass class Animal{ public void eat(Food f) { System.out.println("I am a small animal, eating" + f.getFood()); }}class Cat extends Animal{ public void eat(Food f){ System.out.println("I am a A little cat is eating " + f.getFood()); }}class Dog extends Animal{ public void eat(Food f){ System.out.println("I am a dog, eating" + f. getFood()); }}// Food and its subclasses class Food{ public String getFood(){ return "things"; }}class Fish extends Food{ public String getFood(){ return n "fish"; }}class Bone extends Food{ public String getFood(){ return "Bone"; }}// Master class Master{ public void feed(Animal an, Food f){ an.eat(f); }} Running results:
I am a small animal, eating things, I am a little cat, eating fish, I am a dog, eating bones
The feed method of the Master class has two parameters, namely the Animal type and the Food type. Because it is the parent class, it can pass instances of the child class to it, so that the Master class does not require multiple methods to feed different animals.
Dynamic binding
In order to understand the nature of polymorphism, let’s talk about the detailed process of Java calling methods below.
1) The compiler checks the declaration type and method name of the object.
Suppose that obj.func(param) is called, obj is an object of the Cat class. It should be noted that there may be multiple methods with names with func but different parameters. For example, methods func(int) and func(String) may exist. The compiler will list all methods named func in Cat class and methods accessing attribute public and named func in its parent class Animal.
In this way, the compiler obtains a list of all possible candidate methods to be called.
2) Next, the editor will check the parameter signature provided when calling the method.
If there is a method in all methods named func that exactly matches the provided parameter signature, then select this method. This process is called overloading resolution. For example, if func("hello") is called, the compiler will select func(String) instead of func(int). Due to the existence of automatic type conversion, for example, int can be converted to double. If no method is found with the same signature as the method parameter signature, type conversion will be performed before searching. If there is no matching type in the end or multiple methods match it, Then compile error.
In this way, the compiler obtains the method name and parameter signature that needs to be called.
3) If the method modifier is private, static, final (static and final will be explained later), or a constructor, then the compiler will be able to know exactly which method should be called. We call this method of calling static static binding.
Correspondingly, the called method depends on the actual type of the object and implements dynamic binding at runtime. For example, when calling func("hello"), the editor will use dynamic binding to generate a directive that calls func(String).
4) When the program runs and uses dynamic binding to call the method, the JVM will definitely call the method of the class that is most suitable for the actual type of the object referenced by obj. We have assumed that obj's actual type is Cat, which is a subclass of Animal, and it is called if func(String) is defined in Cat, otherwise it will be searched in the Animal class and its parent class.
Each call to the method requires searching, which is quite expensive. Therefore, the JVM creates a method table (method lable) for each class in advance, which lists the names, parameter signatures and classes to which all methods are named. In this way, when the method is actually called, the virtual machine can only look up this table. In the example above, the JVM searches the method table of the Cat class to find a method that matches the call to func("hello"). This method may be either Cat.func(String) or Animal.func(String). Note that if super.func("hello") is called, the compiler will search the parent class's method table.
Assuming that the Animal class contains three methods: cry(), getName(), and getAge(), then its method table is as follows:
cry() -> Animal.cry()
getName() -> Animal.getName()
getAge() -> Animal.getAge()
In fact, Animal also has a default parent class Object (which will be explained later), which will inherit the Object method, so the methods listed above are not complete.
Assuming that the Cat class overrides the cry() method in the Animal class and adds a new method climbTree(), its parameter list is:
cry() -> Cat.cry()
getName() -> Animal.getName()
getAge() -> Animal.getAge()
climbTree() -> Cat.climbTree()
When running, the process of calling the obj.cry() method is as follows:
The JVM first accesses the method table of the actual type of obj, which may be the method table of the Animal class, or the method table of the Cat class and its subclasses.
JVM searches for a method matching cry() in the method table, and after finding it, you will know which class it belongs to.
The JVM calls this method.