Home  >  Article  >  Java  >  Detailed explanation of type conversion and dynamic binding of Java polymorphic objects

Detailed explanation of type conversion and dynamic binding of Java polymorphic objects

高洛峰
高洛峰Original
2017-01-19 13:45:261332browse

Type conversion of Java polymorphic objects
The object type conversion mentioned here refers to objects with inheritance relationships, not objects of any type. When casting an object that does not have an inheritance relationship, the java runtime will throw a java.lang.ClassCastException exception.

In the inheritance chain, we call the conversion of a subclass to a parent class "upward transformation", and the conversion of a parent class to a subclass is called "downcasting".

Many times, we will define variables as the type of the parent class, but refer to the object of the subclass. This process is upward transformation. When the program is running, the calling of subclass methods is implemented through dynamic binding, which is polymorphism.

However, sometimes in order to complete some functions that the parent class does not have, we need to convert the subclass object after upward transformation into a subclass and call the method of the subclass. This is downward transformation.

Note: You cannot directly force the object of the parent class to the subclass type. You can only convert the up-cast subclass object to the subclass type again. In other words, subclass objects must be transformed upwards before they can be transformed downwards. Please look at the following code:

public class Demo {
  public static void main(String args[]) {
    SuperClass superObj = new SuperClass();
    SonClass sonObj = new SonClass();
    // 下面的代码运行时会抛出异常,不能将父类对象直接转换为子类类型
    // SonClass sonObj2 = (SonClass)superObj;
    // 先向上转型,再向下转型
    superObj = sonObj;
    SonClass sonObj1 = (SonClass)superObj;
  }
}
class SuperClass{ }
class SonClass extends SuperClass{ }

If you remove the comment on line 7, an exception will be thrown during runtime, but the compilation will pass.

Because there are risks in downward transformation, when receiving a reference from 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 不是 SonClass 类的实例
    if(superObj instanceof SonClass){
      SonClass sonObj1 = (SonClass)superObj;
    }else{
      System.out.println("①不能转换");
    }
    superObj = sonObj;
    // superObj 是 SonClass 类的实例
    if(superObj instanceof SonClass){
      SonClass sonObj2 = (SonClass)superObj;
    }else{
      System.out.println("②不能转换");
    }
  }
}
class SuperClass{ }
class SonClass extends SuperClass{ }

Running result:

①不能转换

Summary: The type conversion of the object is checked when the program is running. Upcasting will be performed automatically. The object converted down must be of the current reference type. Subclass.

Java polymorphism and dynamic binding
In Java, variables of the parent class can refer to instances of the parent class or instances of the subclass.

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{
  // 动物的叫声
  public void cry(){
    System.out.println("不知道怎么叫");
  }
   
}
class Cat extends Animal{
  // 猫的叫声
  public void cry(){
    System.out.println("喵喵~");
  }
}
class Dog extends Animal{
  // 狗的叫声
  public void cry(){
    System.out.println("汪汪~");
  }
}

Running result:

不知道怎么叫
喵喵~
汪汪~

The above code defines three The classes are Animal, Cat and Dog respectively. Both the Cat and Dog classes inherit from the Animal class. The obj variable is of type Animal and it can point either to an instance of the Animal class or to an instance of the Cat and Dog classes, which is correct. In other words, variables of the parent class can refer to instances of the parent class or instances of the subclass. Note that the converse is wrong because all cats are animals, but not all animals are cats.

It can be seen that obj can be a human, a cat, or a dog. It has different expressions, which is called polymorphism. Polymorphism means that a thing has different manifestations or forms.

Another example is "human being", which also has many different expressions or realizations. He can be a driver, teacher, doctor, etc. When you hate yourself, you will say "become a new person in the next life", then you will become a driver or teacher in the next life. , doctors can do it, we just say that "human beings" have polymorphism.

There are three necessary conditions for the existence of polymorphism: inheritance, overwriting, and parent class variables refer to subclass objects.

When calling a method using polymorphism:
First check whether the method exists in the parent class. If not, a compilation error occurs; if so, check whether the subclass overrides the method.
If the subclass overrides this method, call the subclass method, otherwise call the parent class method.

As can be seen from the above example, one benefit of polymorphism is: 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 different subclasses. Example. Please look at the following example again:

public class Demo {
  public static void main(String[] args){
    // 借助多态,主人可以给很多动物喂食
    Master ma = new Master();
    ma.feed(new Animal(), new Food());
    ma.feed(new Cat(), new Fish());
    ma.feed(new Dog(), new Bone());
  }
}
// Animal类及其子类
class Animal{
  public void eat(Food f){
    System.out.println("我是一个小动物,正在吃" + f.getFood());
  }
}
class Cat extends Animal{
  public void eat(Food f){
    System.out.println("我是一只小猫咪,正在吃" + f.getFood());
  }
}
class Dog extends Animal{
  public void eat(Food f){
    System.out.println("我是一只狗狗,正在吃" + f.getFood());
  }
}
// Food及其子类
class Food{
  public String getFood(){
    return "事物";
  }
}
class Fish extends Food{
  public String getFood(){
    return "鱼";
  }
}
class Bone extends Food{
  public String getFood(){
    return "骨头";
  }
}
// Master类
class Master{
  public void feed(Animal an, Food f){
    an.eat(f);
  }
}

Run result:

我是一个小动物,正在吃事物
我是一只小猫咪,正在吃鱼
我是一只狗狗,正在吃骨头

The feed method of the Master class has two parameters, namely Animal Type and Food type, because it is a parent class, you can pass instances of subclasses to it, so that the Master class does not need multiple methods to feed different animals.
Dynamic Binding

In order to understand the nature of polymorphism, let’s talk about the detailed process of calling methods in Java.

1) The compiler looks at the declared type and method name of the object.

Assume that obj.func(param) is called, and obj is an object of Cat class. It should be noted that there may be multiple methods named func but with different parameter signatures. For example, there might be methods func(int) and func(String). The compiler will enumerate all the methods named func in the Cat class and the methods named func in its parent class Animal whose access properties are public.

In this way, the compiler obtains a list of all candidate methods that may be called.

2) Next, the editor will check the parameter signatures provided when calling the method.

If there is a method among all methods named func that exactly matches the provided parameter signature, then this method is selected. This process is called overloading resolution. For example, if you call func("hello"), the compiler will choose func(String) instead of func(int). Due to the existence of automatic type conversion, for example, int can be converted to double. If a method with the same signature as the calling method parameter is not found, type conversion will be performed and then the search will continue. If there is no matching type in the end or there are multiple methods matching it, Then there is a compilation error.

In this way, the compiler obtains the method name and parameter signature that needs to be called.

3) If the modifier of the method is private, static, final (static and final will be explained later), or a constructor method, then the compiler will know exactly which method should be called. We will call this method It's called static binding.

Correspondingly, the method called depends on the actual type of the object and is dynamically bound at runtime. For example, when func("hello") is called, the editor will use dynamic binding to generate an instruction to call func(String).

4) When the program is running and a method is called using dynamic binding, 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 the actual type of obj is Cat, which is a subclass of Animal, and if func(String) is defined in Cat, it will be called, otherwise it will be looked for in the Animal class and its parent class.

Every time a method is called, a search is required, which takes a lot of time. Therefore, the JVM pre-creates a method table (method label) for each class, which lists the names, parameter signatures and The class it belongs to. In this way, when the method is actually called, the virtual machine only needs to look up this table. In the above example, the JVM searches the method table of the Cat class for a method that matches a call to func("hello"). This method may be Cat.func(String) or Animal.func(String). Note that if you call super.func("hello"), the compiler will search the method table of the parent class.

Assume 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 (will be explained later), which will inherit Object methods, so the methods listed above are not complete.

Assume that the Cat class covers the cry() method in the Animal class and adds a new method climbTree(), then its parameter list is:
cry() -> Cat.cry( )
getName() -> Animal.getName()
getAge() -> Animal.getAge()
climbTree() -> Cat.climbTree()

at When running, the process of calling the obj.cry() method is as follows:
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. Once found, it knows which class it belongs to.
JVM calls this method.

For more detailed articles on type conversion and dynamic binding of Java polymorphic objects, please pay attention to the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn