Java Type Erasure Mechanism

Java Generics is a feature introduced from JDK 5. It allows us to use type parameter when defining class and interface. It is extensively used in Java Collection framework. The type erasure concept is one of the most confusing part about Generics. This article illustrates what it is and how to use it.

1. A Common Mistake

In the following example, the method accept accepts a list of Object as its parameter. In the main method, it is called by passing a list of String. Does this work?

public class Main {
	public static void main(String[] args) throws IOException {
		ArrayList<String> al = new ArrayList<String>();
		al.add("a");
		al.add("b");
 
		accept(al);
	}
 
	public static void accept(ArrayList<Object> al){
		for(Object o: al)
			System.out.println(o);
	}
}

It seems fine since Object is a super type of String obviously. However, that will not work. Compilation will not pass, and give you an error at the line of accept(al);:

The method accept(ArrayList < Object > ) in the type Main is not applicable for the arguments 
(ArrayList < String > )

2. List < Object > vs. List < String >

The reason is type erasure. REMEMBER: Java generics is implemented on the compilation level. The byte code generated from compiler does not contain type information of generic type for the run-time execution.

After compilation, both List of Object and List of String become List, and the Object/String type is not visible for JVM. During compilation stage, compiler finds out that they are not the same, then gives a compilation error.

3. Wildcards and Bounded Wildcards

List< ? > – List can contain any type

    public static void main(String args[]) {
    	ArrayList<Object> al = new ArrayList<Object>();
    	al.add("abc");
    	test(al);
    }
 
    public static void test(ArrayList<?> al){
    	for(Object e: al){//no matter what type, it will be Object
    		System.out.println(e);
// in this method, because we don't know what type ? is, we can not add anything to al. 
    	}
    }

Always remember that Generic is a concept of compile-time. In the example above, since we don’t know ?, we can not add anything to al. To make it work, you can use wildcards.

List< Object > - List can contain Object or it's subtype

List< ? extends Number > -  List can contain Number or its subtypes.
List< ? super Number > - List can contain Number or its supertypes.

4. Comparisons

Now we know that ArrayList < String > is NOT a subtype of ArrayList < Object > . As a comparison, you should know that if two generic types have the same parameter, their inheritance relation is true for the types. For example, ArrayList < String > is subtype of Collection< String>.

Arrays are different. They know and enforce their element types at runtime. This is called reification. For example, Object[] objArray is a super type of String[] strArr. If you try to store a String into an array of integer, you will get an ArrayStoreException during run-time.

References:

1. Wildcards
2. Java generics and type erasure
3. Generics gotchas

6 thoughts on “Java Type Erasure Mechanism”

  1. “List – List can contain Number or its subtypes.” This seems incorrect, this list cannot contain any object except null, because it cannot determine which subtypes to contain.

  2. i think it is not correct for 2.List vs. List .because List is compatible for List.
    it is correct that it go aginst the goal of implmenting generic.

  3. Generics are invariant and that is the reason why ArrayList is not a subtype of ArrayList. Invariant property ensures type safety promise of Generics. Pls make the correction in the article. Thanks.

  4. Compilation level and run-time level,that’s the cause.Good article.I think I should do more research on JVM internal mechanism.

Leave a Comment