Java类文件中存储的泛型类型在哪里?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (17)

我很清楚泛型类型在编译时从Java代码中删除。什么信息(属性?)1.5+ JVM用来实现getGenericType等等?

提问于
用户回答回答于

它们存储在Signature属性中; 请参阅更新的Java虚拟机规范的第4.8.8节,以及第4.4.4节中字段类型签名的格式。

这里有一个例子javap -verbose java.util.Map

public interface java.util.Map
  SourceFile: "Map.java"
  Signature: length = 0x2
   00 1E 
  [other attributes omitted]

Signature此处的属性指定(如果您将此读作big-endian,就像JVM类文件格式中的所有整数数量一样)常量池值#30(30 = 0x1E)。所以让我们看看那里:

const #30 = Asciz       <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;;

在4.4.4中规定的语法环境中阅读。所以,这使用了两个类型参数,K extends java.lang.Object并且V extends java.lang.Object。类型本身(Map)也扩展了类java.lang.Object,没有接口。

用户回答回答于

Java泛型确实通过类型擦除来实现,所以字节码中没有类型信息。

例如,让我们看看两个声明List字段的类,一个是泛型​​的,另一个是非泛型的:

class NonGeneric {
    List list;
}

和,

class Generic {
    List<String> list;
}

在这两种情况下,生成的字节码如下所示:

  Code:
   Stack=3, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new #2; //class java/util/ArrayList
   8:   dup
   9:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   12:  putfield    #4; //Field list:Ljava/util/List;
   15:  return

没有提及StringArrayListnor中使用的类型List。所以,我们可以看到泛型确实是通过类型擦除来实现的。

但是,如果我们看一下常量池,我们可以找到一个区别。

非泛型常量池:

  Constant pool:
const #1 = Method   #6.#15; //  java/lang/Object."<init>":()V
const #2 = class    #16;    //  java/util/ArrayList
const #3 = Method   #2.#15; //  java/util/ArrayList."<init>":()V
const #4 = Field    #5.#17; //  NonGeneric.list:Ljava/util/List;
const #5 = class    #18;    //  NonGeneric
const #6 = class    #19;    //  java/lang/Object
const #7 = Asciz    list;
const #8 = Asciz    Ljava/util/List;;
const #9 = Asciz    <init>;
const #10 = Asciz   ()V;
// snip the rest //

通用常量池:

  Constant pool:
const #1 = Method   #6.#17; //  java/lang/Object."<init>":()V
const #2 = class    #18;    //  java/util/ArrayList
const #3 = Method   #2.#17; //  java/util/ArrayList."<init>":()V
const #4 = Field    #5.#19; //  Generic.list:Ljava/util/List;
const #5 = class    #20;    //  Generic
const #6 = class    #21;    //  java/lang/Object
const #7 = Asciz    list;
const #8 = Asciz    Ljava/util/List;;
const #9 = Asciz    Signature;
const #10 = Asciz   Ljava/util/List<Ljava/lang/String;>;;
const #11 = Asciz   <init>;
const #12 = Asciz   ()V;
// snip the rest//

可以看出,在Generic该类中,我们可以看到有两个额外的常量,#9并且#10在常量池中提到了List具有泛型类型String

(并结合我从Chris Jester-Young的回答中学到的新知识)

在类文件的拆卸展望更远的,还有前右为恒定#10参考Code: block的的Generic类:

java.util.List list;
  Signature: length = 0x2
   00 0A 

十六进制值0A10十进制的,它是指常量池#10

const #10 = Asciz   Ljava/util/List<Ljava/lang/String;>;;

因此,来自常量池的信息用于表示一个字段是一个通用类型。

扫码关注云+社区