Type Erasure in Java Generics
Java generics are a compile-time feature. At runtime, the type parameter is erased β List<String> and List<Integer> are both just List in bytecode. This choice kept Java 5 backward-compatible with pre-generics code but leaves you with several restrictions.
What erasure does
// Source
public class Box<T> {
private T value;
public T get() { return value; }
}
// Bytecode (approximately)
public class Box {
private Object value;
public Object get() { return value; }
}
The compiler inserts casts at the call site where needed. Your code appears type-safe; the JVM sees Object.
Consequences
1. Same Class object
List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
a.getClass() == b.getClass(); // true β both ArrayList.class
2. No instanceof on parameterised types
if (x instanceof List<String>) // β compile error
if (x instanceof List<?>) // β
wildcard is fine
3. Can't create an array of a parameterised type
T[] arr = new T[10]; // β
T[] arr = (T[]) new Object[10]; // β
unchecked cast
List<String>[] xs = new List<String>[10]; // β
List<?>[] xs = new List<?>[10]; // β
4. Can't use primitives as type arguments
List<int> xs; // β
List<Integer> xs; // β
β wrapper
5. Can't overload on erased signatures
public void f(List<String> a) { ... }
public void f(List<Integer> b) { ... } // β β both erase to f(List)
Passing the type around β Class<T> token
public <T> T fromJson(String json, Class<T> type) {
...
}
User u = mapper.fromJson(json, User.class);
If you need the runtime type (e.g. Jackson, Gson), pass a Class token or a TypeReference.
Common mistakes
- Unchecked casts of generic collections β
(List<User>) objcompiles but isn't checked. Validate element-by-element at the boundary. - Relying on
T.classβ doesn't exist. The compiler has no runtime access toT. - Mixing raw and parameterised types β defeats the compile-time check and propagates unchecked warnings.
Related
Pillar: Java generics. See also wildcards, raw types.