Type erasure can cause polymorphic conflicts, and the JVM's solution is to generalize theBridging method。
give an example
Now there is this generalized class:
class Pair<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
= value;
}
}
Then a subclass inherits it
class DateInter extends Pair<Date> {
@Override
public void setValue(Date value) {
(value);
}
@Override
public Date getValue() {
return ();
}
}
In this subclass, the generic type of the parent class is set to Pair<Date>, and in the subclass, the two methods of the parent class are overridden, with the original intention of limiting the generic type of the parent class to Date, so that the parameters of the two methods inside the parent class are of type Date.
public Date getValue() {
return value;
}
public void setValue(Date value) {
= value;
}
In fact, after type erasure, the generic types of the parent class are all changed to the original type Object, so the parent class will look like the following after compilation:
class Pair {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
= value;
}
}
Look at the types of the two overridden methods of the subclass: the setValue method, where the parent is of type Object and the subclass is of type Date, with different parameter types, which, if true, would not be overriding at all in a normal inheritance relationship, but overloading. Test this in a main method:
public static void main(String[] args) throws ClassNotFoundException {
DateInter dateInter = new DateInter();
(new Date());
(new Object()); //compilation error
}
If it is overloading, then there are two setValue methods in the subclass, one with parameters of Object type and one of Date type, but there is no such a subclass that inherits from the parent class with parameters of Object type. So it is indeed an override, not an overload.
Why is that?
The reason for this is that the generic type passed into the parent class is Date, Pair<Date>, which is meant to change the generic class to the following:
class Pair {
private Date value;
public Date getValue() {
return value;
}
public void setValue(Date value) {
= value;
}
}
The two methods with parameters of type Date are then overridden in the subclass to implement polymorphism in inheritance.
However, for various reasons, the VM can't change the generic type to Date, it can only erase the type and change it to the primitive type Object, which is intended to be overridden to realize polymorphism, but after the type is erased, it can only become overloaded. In this way, type erasure and polymorphism have a conflict. So the JVM uses a special method to accomplish this function, that is, the bridge method.
principle
Decompile the bytecode of DateInter subclass with javap -c className, the result is as follows:
class extends <> {
();
Code:
0: aload_0
1: invokespecial #8 // Method com/tao/test/Pair."<init>":()V
4: return
public void setValue(); //Our rewrittensetValuemethodologies
Code:
0: aload_0
1: aload_1
2: invokespecial #16 // Method com/tao/test/:(Ljava/lang/Object;)V
5: return
public getValue(); //Our rewrittengetValuemethodologies
Code:
0: aload_0
1: invokespecial #23 // Method com/tao/test/:()Ljava/lang/Object;
4: checkcast #26 // class java/util/Date
7: areturn
public getValue(); //编译时由编译器生成的桥methodologies
Code:
0: aload_0
1: invokevirtual #28 // Method getValue:()Ljava/util/Date 去调用Our rewrittengetValuemethodologies;
4: areturn
public void setValue(); //编译时由编译器生成的桥methodologies
Code:
0: aload_0
1: aload_1
2: checkcast #26 // class java/util/Date
5: invokevirtual #30 // Method setValue:(Ljava/util/Date; 去调用Our rewrittensetValuemethodologies)V
8: return
}
From the result of compilation, it is intended to rewrite the subclasses of setValue and getValue methods, but after decompilation there are actually four methods, in fact, the last two methods, theIt's the bridge method that the compiler generates itself. You can see that the parameter types of the bridge methods are both Object, which means that the real overrides of the two methods of the parent class in the subclass are these two bridge methods that we can't see. The @Oveerride on top of the setvalue and getValue methods is just an illusion. The internal implementation of the bridge methods just goes and calls the two methods it overrides.
So, the VM cleverly uses the bridge method to resolve the conflict between type erasure and polymorphism.
And, there may be a question, the bridge method Object getValue() and Date getValue() in the subclasses exist at the same time, but if it is a regular two methods, their method signatures are the same, if we write our own Java code, such code can not pass the compiler's check (different return values can not be used as a condition for overloading), but the virtual machine allows this, because the virtual machine determines a method by the type of parameters and return type, so the compiler allows itself to do this seemingly "illegal" in order to achieve generic polymorphism. However, the virtual machine allows this, because the virtual machine determines a method by its parameter type and return type, so the compiler allows itself to do this seemingly "illegal" thing in order to realize generic polymorphism, and then leaves it to the virtual machine to make the difference.
About the Author.
From the first-line programmer Seven's exploration and practice, continuous learning iteration in the~
This article is included in my personal blog:https://
Public number: seven97, welcome to follow~