1. 程式人生 > >Java泛型之型別擦除

Java泛型之型別擦除

型別擦除

學過C++模板的,在使用Java泛型的時候,會感覺到有點不疑問,例如:(1)無法定義一個泛型陣列、無法呼叫泛型引數物件中對應的方法(當然,通過extends關鍵字是可以做到,只是比較麻煩);(2)ArrayList<Integer>和ArrayList<String>在執行時的型別是相同的。Java中的泛型有這些問題,是它的實現機制決定的,即“型別擦除”。

  1. 型別擦除的定義:編譯通過後,準備進入JVM執行時,就不再有型別引數的概念,換句話說:每定義一個泛型型別,JVM會自動提供一個對應的原生類;
public class Holder4<T> { private T a; private T b; private T c; public Holder4(T a, T b, T c) { this.a = a; this.b = b; this.c = c; } public T getA() { return a; } public T getB() { return b; } public T getC() { return c; } public void setA(T a) { this.a = a; } public void setB(T b) { this.b = b; } public void setC(T c) { this.c = c; } public static void main(String[] args) { Holder4<Automobile> holder4 = new Holder4<>(new Automobile(),new Automobile(), new Automobile()); Automobile a = holder4.getA(); //編譯器幫忙轉型,不需要顯式轉型 Automobile b = holder4.getB(); Automobile c = holder4.getC(); } } 

在Java中,每定義一個泛型型別,就會自動提供一個對應的原始型別,例如:

public class Holder4Raw {

       private Object a; private Object b; private Object c; public Holder4Raw(Object a, Object b, Object c) { this.a = a; this.b = b; this.c = c; } public Object getA() { return a; } public Object getB() { return b; } public Object getC() { return c; } public void setA(Object a) { this.a = a; } public void setB(Object b) { this.b = b; } public void setC(Object c) { this.c = c; } public static void main(String[] args) { Holder4Raw holder4Raw = new Holder4Raw(new Automobile(),new Automobile(), new Automobile()); Automobile a = (Automobile) holder4Raw.getA(); //顯示的轉型 Automobile b = (Automobile) holder4Raw.getB(); Automobile c = (Automobile) holder4Raw.getC(); } } 
  1. 為什麼選擇這種實現機制?
  • 在Java誕生10年後,才想實現類似於C++模板的概念,即泛型;
  • Java的類庫是Java生態中非常寶貴的財富,必須保證向後相容(即現有的程式碼和類檔案依舊合法)和遷移相容(泛化的程式碼和非泛化的程式碼可互相呼叫)基於上面這兩個背景和考慮,Java設計者採取了“型別擦除”這種折中的實現方式。

 

  1. Java泛型依賴編譯器實現,只存在於編譯期,JVM中沒有泛型的概念;那麼,編譯器做了什麼工作呢?(1)set方法是編譯期檢查;(2)get方法的返回值進行轉型,編譯器插入了一個checkcast語句。

我們通過位元組碼進行觀察,可以看出:(1)Holder4和Holder4Raw兩個類的位元組碼完全相同;(2)在main函式的33、41和49行就是編譯器插入的checkcast語句;

public class org.java.learn.generics.Holder4<T> { public org.java.learn.generics.Holder4(T, T, T); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: aload_1 6: putfield #2 // Field a:Ljava/lang/Object; 9: aload_0 10: aload_2 11: putfield #3 // Field b:Ljava/lang/Object; 14: aload_0 15: aload_3 16: putfield #4 // Field c:Ljava/lang/Object; 19: return public T getA(); Code: 0: aload_0 1: getfield #2 // Field a:Ljava/lang/Object; 4: areturn public T getB(); Code: 0: aload_0 1: getfield #3 // Field b:Ljava/lang/Object; 4: areturn public T getC(); Code: 0: aload_0 1: getfield #4 // Field c:Ljava/lang/Object; 4: areturn public void setA(T); Code: 0: aload_0 1: aload_1 2: putfield #2 // Field a:Ljava/lang/Object; 5: return public void setB(T); Code: 0: aload_0 1: aload_1 2: putfield #3 // Field b:Ljava/lang/Object; 5: return public void setC(T); Code: 0: aload_0 1: aload_1 2: putfield #4 // Field c:Ljava/lang/Object; 5: return public static void main(java.lang.String[]); Code: 0: new #5 // class org/java/learn/generics/Holder4 3: dup 4: new #6 // class org/java/learn/generics/Automobile 7: dup 8: invokespecial #7 // Method org/java/learn/generics/Automobile."<init>":()V 11: new #6 // class org/java/learn/generics/Automobile 14: dup 15: invokespecial #7 // Method org/java/learn/generics/Automobile."<init>":()V 18: new #6 // class org/java/learn/generics/Automobile 21: dup 22: invokespecial #7 // Method org/java/learn/generics/Automobile."<init>":()V 25: invokespecial #8 // Method "<init>":(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V 28: astore_1 29: aload_1 30: invokevirtual #9 // Method getA:()Ljava/lang/Object; 33: checkcast #6 // class org/java/learn/generics/Automobile,get方法的轉型 36: astore_2 37: aload_1 38: invokevirtual #10 // Method getB:()Ljava/lang/Object; 41: checkcast #6 // class org/java/learn/generics/Automobile,get方法的轉型 44: astore_3 45: aload_1 46: invokevirtual #11 // Method getC:()Ljava/lang/Object; 49: checkcast #6 // class org/java/learn/generics/Automobile,get方法的轉型 52: astore 4 54: return } 

參考資料

  1. 《Java程式設計思想》
  2. 《Effective Java》
  3. 《Java核心技術》

 

原文作者:duqicauc
轉載地址: Java泛型之型別擦除 | Spring For All
from: https://zhuanlan.zhihu.com/p/31741402