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

Category >> Generics  
  • mojiayi

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

  • prasanth jalasutram

    nice article

  • Ric

    There is nothing to do with type erasure. ArrayList is not a subtype of ArrayList for another good reason.