简体   繁体   English

Java Generics Type Erasure字节代码

[英]Java Generics Type Erasure byte code

As per the java documentation on Erasure of Generic Types , 根据关于Erasure of Generic Types的java文档,

Consider the following generic class that represents a node in a singly linked list: 考虑以下表示单链表中节点的泛型类:

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; }
    // ...
}

Because the type parameter T is unbounded, the Java compiler replaces it with Object : 因为类型参数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; }
    // ...
}

But after compilation with Java 1.7.0_11, when I opened it with any decompiler I can see the same code as like source code. 但是在使用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;
  }
}

If Type-Erasure applied at compile then the byte code must not contain Generic information as shown above. 如果在编译时应用Type-Erasure,则字节代码不得包含如上所示的通用信息。 Kindly clarify me. 请澄清我。

NOTE: I am using JD-GUI as a decompiler to analyze the byte code 注意:我使用JD-GUI作为反编译器来分析字节码

The bytecode contains meta information about the code itself, such as generic types (or variable names) - it does not mean it's useable by the JVM. 字节码包含有关代码本身的元信息,例如泛型类型(或变量名称) - 它并不意味着JVM可以使用它。

The disassembled bytecode of your class looks like below (you can see it with javap -c Node.class ): 你的类的反汇编字节码如下所示(你可以用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
}

You can see that the methods and arguments generic types are there but the code itself refers to Object as expected due to the erasure process. 您可以看到方法和参数泛型类型存在,但由于擦除过程,代码本身会按预期引用Object。

The fact that the class is generic is retained. 保留了类是通用的这一事实。 For instance, at runtime you can call 例如,在运行时您可以调用

Node.class.getTypeParameters()

The next bit of code will return "T". 下一位代码将返回“T”。

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

You can't get the value of type parameters at runtime, but the JVM knows they're there. 您无法在运行时获取类型参数的值,但JVM知道它们在那里。

Erasure comes into play when you construct a instance. 构造实例时,擦除会发挥作用。

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

Becomes

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

Generic classes are always generic. 通用类总是通用的。 But instances do not carry generic type information inside them. 但是实例不会在其中包含泛型类型信息。 The compiler verifies that everything you've done agrees with the generic type and then inserts casts. 编译器会验证您所做的所有事情是否与泛型类型一致,然后插入强制转换。

Generic type information is still saved in the bytecode, specifically in the signature information of the class members. 通用类型信息仍保存在字节码中,特别是在类成员的签名信息中。

For example, executing javap -verbose Node.class yields: 例如,执行javap -verbose Node.class产生:

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

See this section from the JVM specification : 请参阅JVM规范中的此部分

Signatures encode declarations written in the Java programming language that use types outside the type system of the Java Virtual Machine. 签名编码以Java编程语言编写的声明,这些声明使用Java虚拟机类型系统之外的类型。 They support reflection and debugging, as well as compilation when only class files are available. 它们支持反射和调试,以及只有类文件可用时的编译。

A Java compiler must emit a signature for any class, interface, constructor, method, or field whose declaration uses type variables or parameterized types. Java编译器必须为其声明使用类型变量或参数化类型的任何类,接口,构造函数,方法或字段发出签名。 Specifically, a Java compiler must emit: 具体来说,Java编译器必须发出:

  • A class signature for any class or interface declaration which is either generic, or has a parameterized type as a superclass or superinterface , or both. 任何类或接口声明的类签名,它是通用的,或者具有参数化类型作为超类或超接口 ,或两者。

  • A method signature for any method or constructor declaration which is either generic, or has a type variable or parameterized type as the return type or a formal parameter type , or has a type variable in a throws clause, or any combination thereof. 任何方法或构造函数声明的方法签名,它是通用的,或者具有类型变量或参数化类型作为返回类型或形式参数类型 ,或者在throws子句中具有类型变量,或者它们的任何组合。

Everyone, 大家,

I hope it is the decompiler issue only ie JD-GUI . 我希望它只是反编译器问题,即JD-GUI

When I opened with different decompiler ie JDecompiler , I can able to see the expected Bytecode as follows: 当我用不同的反编译器即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