2015-03-31 110 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; 
      } 
}