I've thought java erasure wipes generic types out in compile time however when i test it by myself i realized there are some information about generic types in Bytecode.
here is my test :
i wrote 2 classes:
import java.util.*;
public class Test {
List integerList;
}
and
import java.util.*;
public class Test {
List<Integer> integerList;
}
i compiled both classes and somewhere in generic class i saw this line
integerList{blah blah}Ljava/util/List;{blah blah}
Signature{blah blah}%Ljava/util/List<Ljava/lang/Integer;>;{blah blah}<init>
in non generic class :
integerList{blah blah}Ljava/util/List;{blah blah}<init>
so obviously i have generic information inside bytecode so what is this erasure thing ??
Type info will be erased from here
in the bytecode it will be equivalent to
and there is no chance to know in runtime from integerList object what was its compile time type.
Some Generic type information is stored in
Signature
attributes . Refer JLS 4.8 and 4.6 and JVM spec 4.3.4. Read here:and Neal Gafter's blog.
This is one instance where accurate use of terminology actually matters: Bytecode is the instruction set of the Java Virtual Machine. A class file contains bytecode, but also information used for linking (field signatures, method signatures, ...), for the bytecode verifier, for the debugger, ...
Type erasure means that generic type informations is not translated into byte code; more specifically, all instances of a generic type share the same representation in byte code. Likewise, the dynamic type of an object the runtime keeps track of (as used by the cast and instanceof operators, and available through getClass()) is the same for all instances of a generic class, irrespective of any type parameters supplied in the source code.
Your experiment proves that generic type information is retained in the class file, more specifically, in the types of method and field signatures. That's unsurprising, because the signatures are actually used at compile time. The might also be used at link time, and are even accessible through the reflection api. The crucial difference is that they are the declared types of fields or methods, not the runtime types of actual objects.
That is, since Java 1.5 we must distinguish between a variable's declared type, and the runtime type of the object it refers to. The former supports generics, the latter does not. And yes, this means there isn't a one-to-one correspondence between compile time and runtime types.
Erasure is a mapping from generic to raw types. The common phrase "because of erasure" is essentially meaningless. What is significant are the specifications that use the mapping.
There are two interesting uses.
It's used to map method signatures from using generics to raw types. It is the raw-type signatures that used for overloading. This causes the vast majority of the problems with "erasure". For instance, you can't have two methods
add(List<String>)
andadd(List<Integer>)
in the same type. Overloading probably isn't a great idea, and there's not a great willingness to add this feature.The type available for an instance of an object at runtime is the type it was created with erased. So if you cast to, say,
(String)
that will be checked at runtime, but if you cast toList<String>
only the erasure of that type (List
) will be checked. You can have the variables of typeList<String>
andList<Integer>
point to exactly the same instance. In practice, you shouldn't be using casts (of reference types) in 1.5 and later.Where practical, generic information is kept in class files and made available through reflection. So you'll find it on class definitions, supertypes, fields, methods, constructors, etc.
Erasure means that generic typing is not incorporated in the byte code (when the list is created or used).
The signature you see is used just to indicate that the field is generic.