Type Erasure
Type Erasure
Generics in Java are a compile-time feature. Once the compiler has checked that your code is type-safe, it removes all generic type information from the bytecode. This process is called type erasure. Understanding erasure explains many "surprising" generic behaviours — from runtime instanceof limits to the existence of bridge methods.
What Actually Happens at Compile Time
When you write a generic class the compiler does two things: it checks types, then it rewrites the bytecode using the raw type. If a type parameter is unbounded, it is replaced by Object; if it has a bound (e.g. T extends Comparable<T>) it is replaced by the leftmost bound.
The compiler also inserts unchecked casts at every call site where you read a typed value back out, so the cast happens once, in your code, not deep inside the library.
Box<String> and Box<Integer> are the same class at runtime. There is only one .class file — Box.class — and both instances are instances of it.
Consequences: What You Cannot Do at Runtime
Because type parameters vanish from bytecode, you cannot use them in ways that require runtime type information:
- Cannot create generic arrays:
new T[10]is illegal because the JVM does not know whatTis at runtime. - Cannot use
instanceofwith a type parameter:obj instanceof Twill not compile. - Cannot do
new T(): the JVM would not know which constructor to call. - Generic type parameters are not reified — you cannot ask, for example,
List<String>.class; that expression does not exist. OnlyList.classdoes.
Bridge Methods
Erasure creates a subtle problem for inheritance. Consider a generic interface:
Now you write a concrete implementation:
The method you wrote has the signature process(String), but the erased interface demands process(Object). These are different signatures — polymorphism would break. To fix this, the compiler automatically generates a bridge method:
You can see bridge methods in the bytecode with javap -v StringProcessor — they appear with the ACC_BRIDGE and ACC_SYNTHETIC flags. They are invisible in source code but are real methods in the .class file.
method.isBridge() if you are building a framework or annotation processor.
Heap Pollution and Unchecked Warnings
Heap pollution occurs when a variable of a parameterised type refers to an object that is not of that type. It can happen when you mix raw types with generics, or when you use @SuppressWarnings("unchecked") recklessly.
The cast the compiler inserted fails at the get call, not where the 42 was added — making the source of the bug harder to find. This is why you should never mix raw types and parameterised types in new code.
Bounded Erasure
When a type parameter has a bound, the erased type is the bound, not Object:
This is important because it means the compiler can verify that methods on the bound — like compareTo — are valid calls even after erasure.
Summary
Type erasure is Java's backward-compatibility solution: generics were added in Java 5 without changing the JVM, so all generic information exists only in the compiler. The runtime sees raw types; the compiler inserts casts and generates bridge methods to keep everything working. Understanding erasure prevents runtime surprises and helps you read error messages and reflection output with confidence.