2015-03-31 108 views
10

按照上Erasure of Generic Types Java文檔,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; } 
    // ... 
} 

因爲類型參數Ť Java編譯器將其替換爲對象

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作爲一個反編譯器來分析字節碼

+0

你能不能修復該鏈接到你的反編譯器? – 2015-03-31 18:18:16

+2

知道一個類型是通用的,並且能夠知道具體的泛型類型是有區別的。 – 2015-03-31 18:18:45

+1

擦除擦除_objects_的通用類型信息,而不是_types._ – 2015-03-31 20:56:06

回答

3

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

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

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

this section from the JVM specification

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

一個Java編譯器必須對任何類,接口,構造,方法或字段,其聲明使用類型的變量或參數化類型發射的簽名。具體而言,一個Java編譯器必須發射:

  • 任何類或接口聲明其是通用的,或具有一個參數化的類型作爲超類或超,或者A類簽名。

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

5

字節碼包含的代碼本身,如泛型類型(或變量名)元信息 - 它並不意味着它是由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 
} 

你可以看到,方法和參數的泛型類型是有,但代碼本身是指對象的要求由於擦除過程。

3

保留了該類是通用的事實。例如,在運行時,您可以撥打

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(); 

泛型類總是通用的。但實例不在其中攜帶通用類型信息。編譯器驗證您所做的每件事都與泛型類型一致,然後插入強制轉換。

0

大家,

我希望這是反編譯問題只即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; 
      } 
}