2009-06-02 102 views

回答

29

它们存储在Signature属性中;请参阅updated Java Virtual Machine Specification的第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属性在这里指定(如果你看这是大端,就像在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.ObjectV extends java.lang.Object。类型本身(Map)也延伸类java.lang.Object,并且没有接口。

14

Java泛型确实由type erasure实现,所以在字节码中没有类型信息。

例如,让我们来看看它声明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 

没有引用在ArrayListList中使用的String类型。所以,我们可以看到泛型确实是通过类型擦除来实现的。

但是,如果我们看一下常量池,我们可以找到差异。

非通用常量池:

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's answer教训)

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

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

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

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

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