问题引出
泛型父类
public class GenericityErasure<T> {
public T f1(T t){
System.out.println("f1");
return t;
}
public T f2(T t){
System.out.println("GenericityErasure.f2");
return t;
}
public Object f3(Object o){
System.out.println("GenericityErasure.f3");
return o;
}
}
子类复写泛型方法 f1
public class GenericityErasureSub extends GenericityErasure<Date> {
public Date f1(Date localDate) {
System.out.println("sub");
return localDate;
}
public Object f3(Date o) {
System.out.println("GenericityErasureSub.f3");
return o;
}
}
上层调用代码
class GenericityErasureSubTest {
@Test
void GenericityErasureSub() {
GenericityErasureSub sub = new GenericityErasureSub();
GenericityErasure father = null;
father = sub;
sub.f1(new Date());
father.f1(new Object());
father.f1(new Date());
father.f3(new Object());
father.f3(new Date());
sub.f3(new Object());
sub.f3(new Date());
}
}
出现问题:
GenericityErasureSub.java:11 是找个类声明的行。
问题总结:
1、f3方法属于重载运行时能够区分调用不同的方法,为啥泛型擦除后的f1方法不行呢?
2、类型转换异常是具体走到哪行代码抛出的呢?为啥在类头上标记位置呢?
问题分析
以此查看这三个类生成的字节码文件,其中泛型子类的的字节码有点不一致。
idea上的字节码如下:
public class lxf/experiment/hashcode/GenericityErasureSub extends lxf/experiment/hashcode/GenericityErasure {
// compiled from: GenericityErasureSub.java
// access flags 0x1
public <init>()V
L0
LINENUMBER 11 L0
ALOAD 0
INVOKESPECIAL lxf/experiment/hashcode/GenericityErasure.<init> ()V
RETURN
L1
LOCALVARIABLE this Llxf/experiment/hashcode/GenericityErasureSub; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x1
public f1(Ljava/util/Date;)Ljava/util/Date;
L0
LINENUMBER 14 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "sub"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
LINENUMBER 15 L1
ALOAD 1
ARETURN
L2
LOCALVARIABLE this Llxf/experiment/hashcode/GenericityErasureSub; L0 L2 0
LOCALVARIABLE localDate Ljava/util/Date; L0 L2 1
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x1
public f3(Ljava/util/Date;)Ljava/lang/Object;
L0
LINENUMBER 18 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "GenericityErasureSub.f3"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
LINENUMBER 19 L1
ALOAD 1
ARETURN
L2
LOCALVARIABLE this Llxf/experiment/hashcode/GenericityErasureSub; L0 L2 0
LOCALVARIABLE o Ljava/util/Date; L0 L2 1
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x1041
public synthetic bridge f1(Ljava/lang/Object;)Ljava/lang/Object;
L0
LINENUMBER 11 L0
ALOAD 0
ALOAD 1
CHECKCAST java/util/Date
INVOKEVIRTUAL lxf/experiment/hashcode/GenericityErasureSub.f1 (Ljava/util/Date;)Ljava/util/Date;
ARETURN
L1
LOCALVARIABLE this Llxf/experiment/hashcode/GenericityErasureSub; L0 L1 0
MAXSTACK = 2
MAXLOCALS = 2
}
第一眼不容易看出问题(注意看最后的方法)。通过javap 查看:
子类中只有一个f1方法,但是字节码文件中竟然多了一个f1方法。
问题总结:
为了解决泛型方法被子类复写,然后泛型的特性跟java多态的冲突。java在编译阶段重新自动复写了父类的方法,进行强制类型转换后调用子类的方法。
这个编译器自动生成的方法叫做桥方法。做的事情就是入参类型强制转换,然后调用子类的方法。