泛型擦除后仍然保留的泛型信息
我们知道泛型擦除会擦除泛型类型,转而使用原始类型或类型上界。那么有关泛型的信息在编译完成后一点都不会保留吗?其实,还是有保留的,这些信息保留在字节码指令集之外的地方。
具体哪些泛型信息会被保留呢?总结来说就是声明、定义处的泛型信息会被保留,比如:泛型接口、类、方法定义、成员变量的声明等。
代码验证
先准备一个接口和一个实现类
interface Animal<T, K> { void eat(K k); } class Dog<T, K> implements Animal<T, K> { private T t; ArrayList<T> list = new ArrayList<>(); @Override public void eat(K k) { List<K> list = new ArrayList<>(); list. Add(k); } }
执行以下命令查看接口的字节码信息
javap -c -p -s Animal.class
Compiled from "GenericErasureExample2.java" interface com.foo.Animal<T, K> { public abstract void eat(K); descriptor: (Ljava/lang/Object;)V }
从接口的字节码信息中,可以看到接口Animal定义的泛型 T、K都在声明处被保留,方法的形参类型K 也被保留。
接下来看一下实现类Dog
javap -c -p -s Dog.class
Compiled from "GenericErasureExample2.java" class com.foo.Dog<T, K> implements com.foo.Animal<T, K> { private T t; descriptor: Ljava/lang/Object; java.util.ArrayList<T> list; descriptor: Ljava/util/ArrayList; com.foo.Dog(); descriptor: ()V Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: new #2 // class java/util/ArrayList 8: dup 9: invokespecial #3 // Method java/util/ArrayList."<init>":()V 12: putfield #4 // Field list:Ljava/util/ArrayList; 15: return public void eat(K); descriptor: (Ljava/lang/Object;)V Code: 0: new #2 // class java/util/ArrayList 3: dup 4: invokespecial #3 // Method java/util/ArrayList."<init>":()V 7: astore_2 8: aload_2 9: aload_1 10: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 15: pop 16: return }
Dog的输出结果,不仅类的声明处,泛型信息得以保留,而且实现接口的声明处泛型也保留了。
成员变量 t和 list 的泛型信息也是保留的,方法的形参类型也是保留的,构造函数也是一样会保留,和方法同理。
既然保留了泛型信息,那么能获取到泛型的具体类型吗
如果你想用反射的方式获取泛型的具体类型是做不到的,能获取到的仅仅是泛型的定义,比如<T>。
因为Java实现泛型的原理是泛型擦除,所以不可能知道泛型的具体类型。