Java泛型类型擦除字节码

Java泛型类型擦除字节码,java,type-erasure,generics,Java,Type Erasure,Generics,根据上的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 d

根据上的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; }
    // ...
}
但是在用Java1.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;
  }
}
公共类节点
{
私有T数据;
私有节点下一步;
公共节点(T参数,Node参数节点)
{
this.data=paramT;
this.next=paramNode;
}
公共T getData()
{
返回此.data;
}
}
如果在编译时应用类型擦除,则字节码不得包含如上所示的通用信息。请澄清


注意:我使用反编译器来分析字节码

泛型类型信息仍然保存在字节码中,特别是类成员的签名信息中

例如,执行
javap-verbose Node.class
会产生:

 ...
 LocalVariableTypeTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   Ltest/Node<TT;>;
。。。
LocalVariableTypeTable:
起始长度插槽名称签名
0 5 0此Ltest/节点;
见:

签名对用Java编程语言编写的声明进行编码,这些声明使用Java虚拟机类型系统之外的类型。它们支持反射和调试,以及在只有类文件可用时进行编译

Java编译器必须为其声明使用类型变量或参数化类型的任何类、接口、构造函数、方法或字段发出签名。具体而言,Java编译器必须发出:

  • 任何类或接口声明的类签名,该类或接口声明要么是泛型的,要么具有作为超类或超接口的参数化类型,要么两者兼而有之

  • 任何方法或构造函数声明的方法签名,该方法或构造函数声明是泛型的,或具有类型变量或参数化类型作为返回类型或形式参数类型,或在throws子句中具有类型变量,或其任意组合


字节码包含关于代码本身的元信息,例如泛型类型(或变量名)——这并不意味着JVM可以使用它

您的类的反汇编字节码如下所示(您可以通过
javap-c Node.class
看到它):

公共类节点{
公共节点(T,Node);
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:aload_0
5:aload_1
6:putfield#2//字段数据:Ljava/lang/Object;
9:aload_0
10:aload_2
11:putfield#3//字段next:LNode;
14:返回
公共T getData();
代码:
0:aload_0
1:getfield#2//字段数据:Ljava/lang/Object;
4:轮到你了
}

您可以看到方法和参数的泛型类型存在,但由于擦除过程,代码本身引用了预期的对象。

保留了类是泛型的事实。例如,在运行时,您可以调用

Node.class.getTypeParameters()
下一位代码将返回“T”

泛型类总是泛型的。但实例中不包含泛型类型信息。编译器验证您所做的一切是否与泛型类型一致,然后插入强制类型转换。

Everyone

我希望这只是反编译器的问题,即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;
            }
}

你能修复反编译器的链接吗?知道一个类型是泛型和知道具体的泛型类型是有区别的。擦除会擦除对象的泛型类型信息,而不是类型。嘿!Daniel Earwicker,反编译器链接是fixedit,这似乎比详细的更有趣
Node.class.getTypeParameters()
(new Node<Integer>()).getClass().getTypeParameters()[0].getName()
Node<Integer> node = new Node<Integer>(1, null);
Integer i = node.getData();
Node node = new Node(1, null);
Integer i = (Integer)node.getData();
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;
            }
}