簡體   English   中英

java泛型類型擦除

[英]java generics type erasure

當聲明類型參數<T extends A & B & C>的類<T extends A & B & C> ,類型擦除過程將用類型A替換T 但是如果T類型的變量調用接口BC方法聲明,Java會用它做什么?

當我們創建一個泛型類型的實例,如ArrayList<String> ,類型參數String也將被刪除,但是調用get方法將返回類型String ,這些信息來自哪里,因為它已被刪除?

我知道使用java反射,但我需要一些具體的解釋。

不使用反射 - 鑄造是。 例如,請使用以下代碼:

interface A {
    void a();
}

interface B {
    void b();
}

interface C {
    void c();
}

class Generic<T extends A & B & C> {
    T t;

    Generic(T t) {
        this.t = t;
    }

    void callMethods() {
        t.a();
        t.b();
        t.c();
    }
}

現在看一下Generic的字節碼(刪除了構造函數):

class Generic extends java.lang.Object{
A t;

void callMethods();
  Code:
   0:   aload_0
   1:   getfield        #2; //Field t:LA;
   4:   invokeinterface #3,  1; //InterfaceMethod A.a:()V
   9:   aload_0
   10:  getfield        #2; //Field t:LA;
   13:  checkcast       #4; //class B
   16:  invokeinterface #5,  1; //InterfaceMethod B.b:()V
   21:  aload_0
   22:  getfield        #2; //Field t:LA;
   25:  checkcast       #6; //class C
   28:  invokeinterface #7,  1; //InterfaceMethod C.c:()V
   33:  return    
}

請注意每個invokeinterface調用b()c()之前的checkcast指令。

結果就像Generic實際上是這樣編寫的:

class Generic<T extends A> {
    T t;

    Generic(T t) {
        this.t = t;
    }

    void callMethods() {
        t.a();
        ((B) t).b();
        ((C) t).c();
    }
}

至於你關於ArrayList的問題 - 關於作為列表元素類型的get()的返回類型的信息仍然存儲為ArrayList類的一部分。 編譯器將再次在調用代碼中插入強制轉換,因此:

ArrayList<String> strings = new ArrayList<String>();
strings.add("foo");
String x = strings.get(0);

在執行時相當於:

ArrayList strings = new ArrayList();
strings.add("foo");
String x = (String) strings.get(0);

這個的一個重要方面是你不能在執行時詢問一個ArrayList對象什么T - 該信息已被刪除。

在編譯器完成類型檢查后,類型擦除有效地用Object替換所有泛型類型參數。 在你的<T extends A & B & C>示例中, A就像其他所有內容一樣被刪除。

當您在ArrayList<String>上調用get時,編譯器會生成字節碼,將返回的對象自動轉換為字符串。 列表本身並不“知道”它是一個字符串列表(由於類型擦除),但是調用get的代碼知道它希望從列表中get的東西是一個字符串。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM