前因
一直以來大家都說java的泛型是偽泛型,類型參數(shù)會在編譯階段進(jìn)行擦除,那么到底什么是所謂的泛型擦除呢,怎么去理解它?今天結(jié)合字節(jié)碼來一探究竟。
泛型擦除的含義
泛型中的類型參數(shù)只存在于編譯期,在運(yùn)行時,Java 的虛擬機(jī) ( 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>其實是對應(yīng)到同一個Class對象的,虛擬機(jī)在編譯的時候并沒有生成兩份class文件來對應(yīng)ArrayList<String>,ArrayList<Integer>。
看下上面這個類對應(yīng)的字節(jié)碼,確實是對應(yīng)到同一個符號鏈接。

例子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顯示的用了類型轉(zhuǎn)換來得到塞入的字符串。
String a = (String) holder1.get();
字節(jié)碼中確實生成了checkcast指令:

運(yùn)用了泛型的Holder2,代碼相對來說簡潔一些,并不需顯示的來做類型轉(zhuǎn)換,編譯器自動為我們生成了相應(yīng)的類型轉(zhuǎn)換指令,這同時映證了在運(yùn)行時候jvm確實不知道泛型的類型信息。
字節(jié)碼如下:


參考資料
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