Honestly there’s nth much to type erasure.
For backwards compatibility with pre-generics era code, Java implements generics purely in compile time and the compiled code doesn’t contain any generic implementation. This has some limitations on reflection and but more importantly, does some weird stuff shown bellow.
Generic Types
Generic Types are substituted with Object or if it is bounded, it uses the bounded type.
Erasure of type parameter
Original code:
class MyClass<T> { private T value;}
After type erasure:
class MyClass { private Object value;}
Erasure of bounded type parameter
Java always uses the leftmost bounded type parameter
Original code:
class MyClass<T extends Number & Comparable<T>> { private T value;}
After type erasure:
class MyClass { private Number value;}
Generic Methods
exactly the same as generic types.
Bridge Methods
Due to the fun of type erasure, method overrides can turn into method overloads!
What does that mean?… Let’s see!
Original code:
interface MyInterface<T> { T behaviour1(T a); T behaviour2();}class MyClass implements MyInterface<String> { @Override public String behaviour1(String a) { return "hehe " + a; } @Override public String behaviour2() { return "harhar"; }}
After type erasure:
interface MyInterface { Object behaviour1(Object a); Object behaviour2();}class MyClass implements MyInterface { // THIS IS NO LONGER AN OVERRIDE!!! // Method parameters are invariant, // thus, has a different method signature than the interface. // It has become an overload... public String behaviour(String a) { return "hehe " + a; } // The compiler injects this bridge method // also known as a synthetic method. // This method is the method that overrides the interface method public Object behaviour(Object a) { return behaviour((String) a); } // Java allows for Covariant return types // But, the method signature has changed! // So, a bridge method is required! public String behaviour2() { return "harhar"; } public Object behaviour2() { return behaviour2(); }}
Runtime Implications
class MyClass { <T> T[] returnGTypedArray(T a) { // Declaring an array of the generic type parameter T[] array = {a}; return array; }}
This won’t compile! Since type information is lost at runtime. When the program is compiled, T is actually just replaced with Object. Since this doesn’t guarantee type safety, the compiler doesn’t compile.
List<String> stringList = new ArrayList<>();List<Integer> intList = new ArrayList<>();// At runtime, both have the same typeSystem.out.println(stringList.getClass()); // class java.util.ArrayListSystem.out.println(intList.getClass()); // class java.util.ArrayListSystem.out.println(stringList.getClass() == intList.getClass()); // true
Same goes for Classes/Interfaces that use generic type parameters. They get lost at runtime.