簡體   English   中英

Java Generics Type Erasure字節代碼

[英]Java Generics Type Erasure byte code

根據關於Erasure of Generic Types的java文檔,

考慮以下表示單鏈表中節點的泛型類:

public class Node<T> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) }
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}

因為類型參數T是無界的,所以Java編譯器將其替換為Object

public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}

但是在使用Java 1.7.0_11編譯之后,當我用任何反編譯器打開它時,我可以看到與源代碼相同的代碼。

public class Node<T>
{
  private T data;
  private Node<T> next;

  public Node(T paramT, Node<T> paramNode)
  {
    this.data = paramT;
    this.next = paramNode;
  }

  public T getData()
  {
    return this.data;
  }
}

如果在編譯時應用Type-Erasure,則字節代碼不得包含如上所示的通用信息。 請澄清我。

注意:我使用JD-GUI作為反編譯器來分析字節碼

字節碼包含有關代碼本身的元信息,例如泛型類型(或變量名稱) - 它並不意味着JVM可以使用它。

你的類的反匯編字節碼如下所示(你可以用javap -c Node.class看到它):

public class Node<T> {
  public Node(T, Node<T>);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: aload_1
       6: putfield      #2                  // Field data:Ljava/lang/Object;
       9: aload_0
      10: aload_2
      11: putfield      #3                  // Field next:LNode;
      14: return

  public T getData();
    Code:
       0: aload_0
       1: getfield      #2                  // Field data:Ljava/lang/Object;
       4: areturn
}

您可以看到方法和參數泛型類型存在,但由於擦除過程,代碼本身會按預期引用Object。

保留了類是通用的這一事實。 例如,在運行時您可以調用

Node.class.getTypeParameters()

下一位代碼將返回“T”。

(new Node<Integer>()).getClass().getTypeParameters()[0].getName()

您無法在運行時獲取類型參數的值,但JVM知道它們在那里。

構造實例時,擦除會發揮作用。

Node<Integer> node = new Node<Integer>(1, null);
Integer i = node.getData();

Node node = new Node(1, null);
Integer i = (Integer)node.getData();

通用類總是通用的。 但是實例不會在其中包含泛型類型信息。 編譯器會驗證您所做的所有事情是否與泛型類型一致,然后插入強制轉換。

通用類型信息仍保存在字節碼中,特別是在類成員的簽名信息中。

例如,執行javap -verbose Node.class產生:

 ...
 LocalVariableTypeTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   Ltest/Node<TT;>;

請參閱JVM規范中的此部分

簽名編碼以Java編程語言編寫的聲明,這些聲明使用Java虛擬機類型系統之外的類型。 它們支持反射和調試,以及只有類文件可用時的編譯。

Java編譯器必須為其聲明使用類型變量或參數化類型的任何類,接口,構造函數,方法或字段發出簽名。 具體來說,Java編譯器必須發出:

  • 任何類或接口聲明的類簽名,它是通用的,或者具有參數化類型作為超類或超接口 ,或兩者。

  • 任何方法或構造函數聲明的方法簽名,它是通用的,或者具有類型變量或參數化類型作為返回類型或形式參數類型 ,或者在throws子句中具有類型變量,或者它們的任何組合。

大家,

我希望它只是反編譯器問題,即JD-GUI

當我用不同的反編譯器即JDecompiler打開時,我能夠看到預期的字節碼如下:

public class Node {

            private Object data;
            private Node next;

            public Node(Object obj, Node node) {
/*   7*/        data = obj;
/*   8*/        next = node;
            }

            public Object getData() {
/*  12*/        return data;
            }
}

暫無
暫無

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

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