Why is generic type information visible in class file?

Krzysiek :

According to https://docs.oracle.com/javase/tutorial/java/generics/genTypes.html each non-bounded type should be replaced by Object by compiler.

I have a class:

 public class Test<E> {

    void setE(E e) {
        return;
   }
}

After decompilation in Idea the result is this:

 public class Test<E> {
    public Test() {
    }

    void setE(E e) {
    }
  }

Why is E not replaced with Object?

Michael :

The tutorial is somewhat misleading here. It is trying to make a point about type erasure, but it doesn't do so very elegantly.

The bytecode for your generated class is as follows. Clearly this information is not discarded.

  Last modified 2 Mar 2020; size 332 bytes
  MD5 checksum e3b7faf33fd5666eae578bd516d0f903
  Compiled from "Test.java"
public class Test<E extends java.lang.Object> extends java.lang.Object
  minor version: 0
  major version: 53
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #2                          // Test
  super_class: #3                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 2
Constant pool:
   #1 = Methodref          #3.#15         // java/lang/Object."<init>":()V
   #2 = Class              #16            // Test
   #3 = Class              #17            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               setE
   #9 = Utf8               (Ljava/lang/Object;)V
  #10 = Utf8               Signature
  #11 = Utf8               (TE;)V
  #12 = Utf8               <E:Ljava/lang/Object;>Ljava/lang/Object;
  #13 = Utf8               SourceFile
  #14 = Utf8               Test.java
  #15 = NameAndType        #4:#5          // "<init>":()V
  #16 = Utf8               Test
  #17 = Utf8               java/lang/Object
{
  public Test();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  void setE(E);
    descriptor: (Ljava/lang/Object;)V
    flags: (0x0000)
    Code:
      stack=0, locals=2, args_size=2
         0: return
      LineNumberTable:
        line 3: 0
    Signature: #11                          // (TE;)V
}
Signature: #12                          // <E:Ljava/lang/Object;>Ljava/lang/Object;
SourceFile: "Test.java"

The reason that this information cannot be thrown away in the bytecode is imagine that this class is part of a library; another project depends upon it. At compile-time in that 2nd project, their compiler needs to be able to make assertions about whether the generic type parameter is in bounds. If it had been thrown away completely, they'd have to write

Test test = new Test();

while I, in my source project, could write

Test<String> test = new Test<>():

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=20775&siteId=1