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>) obj compiles but isn't checked. Validate element-by-element at the boundary.
  • Relying on T.class β€” doesn't exist. The compiler has no runtime access to T.
  • Mixing raw and parameterised types β€” defeats the compile-time check and propagates unchecked warnings.

Related

Pillar: Java generics. See also wildcards, raw types.