前因
一直以来大家都说java的泛型是伪泛型,类型参数会在编译阶段进行擦除,那么到底什么是所谓的泛型擦除呢,怎么去理解它?今天结合字节码来一探究竟。
泛型擦除的含义
泛型中的类型参数只存在于编译期,在运行时,Java 的虚拟机 ( JVM ) 并不知道泛型的存在。
例子1
先看如下的代码,你觉得它的输出是'true'还是'false'。
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
}
答案是true,也就是说ArrayList<String>和ArrayList<Integer>其实是对应到同一个Class对象的,虚拟机在编译的时候并没有生成两份class文件来对应ArrayList<String>,ArrayList<Integer>。
看下上面这个类对应的字节码,确实是对应到同一个符号链接。
例子2
再来看一个例子:
public class Holder1 {
private Object a;
public Holder1(Object a) {
this.a = a;
}
public Object get() {
return this.a;
}
public void set(Object a) {
this.a = a;
}
public static void main(String[] args) {
Holder1 holder1 = new Holder1("test");
String a = (String) holder1.get();
System.out.println(a);
}
}
public class Holder2<T> {
private T a;
public Holder2(T a) {
this.a = a;
}
public T get() {
return this.a;
}
public void set(T a) {
this.a = a;
}
public static void main(String[] args) {
Holder2<String> holder2 = new Holder2<>("test");
String a = holder2.get();
System.out.println(a);
}
}
在上面的两个例子中,Holder1用一个Object的变量来持有对象,Holder2用了泛型。在main方法中,Holder1显示的用了类型转换来得到塞入的字符串。
String a = (String) holder1.get();
字节码中确实生成了checkcast指令:
运用了泛型的Holder2,代码相对来说简洁一些,并不需显示的来做类型转换,编译器自动为我们生成了相应的类型转换指令,这同时映证了在运行时候jvm确实不知道泛型的类型信息。
字节码如下:
Holder2的字节码.png Holder2的字节码.png参考资料
1.https://segmentfault.com/a/1190000005179142
2.https://stackoverflow.com/questions/313584/what-is-the-concept-of-erasure-in-generics-in-java
3.Generics in the Java Programming Language
4.The Java Virtual Machine Specification Java SE 8 Edition
网友评论