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

fnatzsnv  于 2021-06-30  发布在  Java
关注(0)|答案(2)|浏览(347)

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

mutmk8jj

mutmk8jj1#

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

没有提到 String 中使用的类型 ArrayList 也不是 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 .
(结合了我从克里斯·杰斯特·杨的回答中学到的新知识)
进一步查看类文件的反汇编,在 Code: blockGeneric 班级:

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

十六进制值 0A10 十进制表示常量池 #10 :

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

因此,使用常量池中的信息来指示字段是泛型类型。

piwo6bdm

piwo6bdm2#

它们储存在 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 ,没有接口。

相关问题