🔹 What is Type Erasure?
Type erasure is a process performed by the Java compiler when working with generics.
In short:
The compiler removes (erases) all generic type information during compilation, replacing it with raw types or bounds (like
Object
or upper bounds if specified).
🔹 Why Does It Exist?
Backward Compatibility
Generics were introduced in Java 5. To maintain compatibility with older versions of Java (pre-Generics), the compiled bytecode could not contain actual type parameters.
This way, code with generics can still run on JVMs that existed before generics were introduced.
🔹 What Happens During Erasure?
Example
List<String> list = new ArrayList<>();
list.add("Hello");
String item = list.get(0);
After type erasure, this is roughly how the compiled code looks like:
List list = new ArrayList();
list.add("Hello");
String item = (String) list.get(0); // Cast inserted by the compiler
🔹 How Does Erasure Work?
There are 3 main rules for type erasure:
1️⃣ Remove Type Parameters
Type parameters like <T>
or <E>
are removed from the compiled code.
Example:
class Box<T> { T value; }
Becomes:
class Box { Object value; }
2️⃣ Replace with Upper Bound (if any)
If the generic type has a bound, it gets replaced by that bound.
Example:
class Box<T extends Number> { T value; }
Becomes:
class Box { Number value; }
3️⃣ Insert Casts Where Necessary
To preserve type safety, the compiler inserts casts where needed.
T value = list.get(0);
Becomes:
Object obj = list.get(0);
T value = (T) obj;
🔹 Key Consequences of Type Erasure
⚠️ No Runtime Generics Information
After erasure, generic type information (like T
or String
) is gone at runtime. This is why:
List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass()); // true
Cannot Use instanceof
with Generics
This won’t compile:
if (list instanceof List<String>) { // Compile error!
}
Because after erasure, List<String>
just becomes List
.
⚠️ Arrays with Generics are Unsafe
This is why you cannot create generic arrays directly:
List<String>[] array = new ArrayList<String>[10]; // Compile error
⚠️ Reflection Loses Type Parameters
When you inspect a generic class using reflection, you cannot see the actual type arguments used at runtime.
✅ Workarounds (Type Tokens)
If you need to retain generic type information (like in frameworks/libraries), you can pass a Class<T> or a TypeReference<T>
to methods so they know the type at runtime.
Example:
public <T> T parse(String json, Class<T> clazz) { ... }
🔹 Quick Summary
What? | After Type Erasure |
---|---|
List<String> | List |
Box<T> | Box |
T | Object (or upper bound) |
Type checking | Happens at compile-time only |