Where are generic types stored in java class files?
They are stored in Signature
attributes; see section 4.8.8 of the updated Java Virtual Machine Specification, as well as section 4.4.4 for the format of the field type signature.
Here's an example using javap -verbose java.util.Map
:
public interface java.util.Map
SourceFile: "Map.java"
Signature: length = 0x2
00 1E
[other attributes omitted]
The Signature
attribute here specifies (if you read this as big-endian, like all integer quantities in the JVM class file format are) constant pool value #30 (30 = 0x1E). So let's have a look there:
const #30 = Asciz <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;;
Read this in the context of the grammar specified in 4.4.4. So, this uses two type parameters, K extends java.lang.Object
and V extends java.lang.Object
. The type itself (Map
) also extends class java.lang.Object
, and no interfaces.
Java generics are indeed implemented by type erasure, so there is no type information in the bytecode.
For example, let's take a look two classes which declare a List
field, one in generic and the other in non-generic form:
class NonGeneric {
List list;
}
And,
class Generic {
List<String> list;
}
In both cases, the resulting bytecode is the following:
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
There is no reference to the String
type used in the ArrayList
nor List
. So, we can see that generics is indeed implemented by type erasure.
However, if we look at the constant pool, we can find a difference.
The non-generic constant pool:
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 //
The generic constant pool:
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//
As can be seen, in the Generic
class, we can see there are two extra constants, #9
and #10
, in the constant pool, which mentions that the List
has the generic type of String
.
(And incorporating new knowledge that I learned from Chris Jester-Young's answer)
Looking further at the disassembly of the class file, there is a reference to the constant #10 right before the Code: block
of the Generic
class:
java.util.List list;
Signature: length = 0x2
00 0A
The hexadecimal value 0A
is 10
in decimal, which refers to the constant pool #10
:
const #10 = Asciz Ljava/util/List<Ljava/lang/String;>;;
Therefore, the information from the constant pool is used in order to indicate that a field is of a generic type.