简体   繁体   中英

Java Generics Type Erasure byte code

As per the java documentation on Erasure of Generic Types ,

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 :

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.

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. Kindly clarify me.

NOTE: I am using JD-GUI as a decompiler to analyze the byte code

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.

The disassembled bytecode of your class looks like below (you can see it with 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.

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".

(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.

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:

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

See this section from the JVM specification :

Signatures encode declarations written in the Java programming language that use types outside the type system of the Java Virtual Machine. 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. Specifically, a Java compiler must emit:

  • 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.

Everyone,

I hope it is the decompiler issue only ie JD-GUI .

When I opened with different decompiler ie JDecompiler , I can able to see the expected Bytecode as follows:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM