Java Theory and Practice: Generics Gotchas
By Brian Goetz2005-03-23
Implications of erasure
Because generics are implemented almost entirely in the Java compiler, and not in the runtime, nearly all type information about generic types has been "erased" by the time the bytecode is generated. In other words, the compiler generates pretty much the same code you would have written by hand without generics, casts and all, after checking the type-safety of your program. Unlike in C++, List<Integer> and List<String> are the same class (although they are different types, both subtypes of List<?> -- a distinction that is more important in JDK 5.0 than in previous versions of the language).
One of the implications of erasure is that a class cannot implement both Comparable<String> and Comparable<Number>, because both of these are in fact the same interface, specifying the same compareTo() method. It might seem sensible to want to declare a DecimalString class that is comparable to both Strings and Numbers, but to the Java compiler, you would be trying to declare the same method twice:
public class DecimalString implements Comparable<Number>, Comparable<String> { ... } // nope
Another consequence of erasure is that using casts or instanceof with generic type parameters doesn't make any sense. The following code will not improve the type safety of your code at all:
public <T> T naiveCast(T t, Object o) { return (T) o; }
The compiler will simply emit an unchecked conversion warning, because it doesn't know if the cast is safe or not. The naiveCast() method will not in fact do any casting at all -- T will simply be replaced with its erasure (Object), and the object passed in will be cast to Object -- not what was intended.
Erasure is also responsible for the construction issues described above -- that you cannot create an object of generic type because the compiler doesn't know what constructor to call. If your generic class needs to be constructing objects whose type is specified by generic type parameters, its constructors should take a class literal (Foo.class) and store it so that instances can be created with reflection.
Tutorial Pages:
» Identify and avoid some of the pitfalls in learning to use generics
» Generics are not covariant
» Construction delays
» Generifying existing classes
» Implications of erasure
» Summary
» Resources
First published by IBM developerWorks
