Type Erasure is the process used by the Java compiler to translate generic code by removing or replacing type parameters. During compilation, type parameters are replaced with their upper bounds, or with Object when no explicit bound is specified.
During type erasure, the compiler:
- Replaces unbounded type parameters with Object.
- Replaces bounded type parameters with their upper bound.
- Inserts type casts where required to preserve type correctness.
- Generates bridge methods when necessary to preserve polymorphism in generic class hierarchies.
Note: Generic type parameters are erased from ordinary runtime object representations. However, some generic type information may still be stored in class-file metadata and can be available through reflection.
How Type Erasure Works
Now, we are going to discuss how Type Erasure works in Java
1. Unbounded Type Example
// Here, T is unbounded (default bound: Object)
class GFG<T>{
T obj;
GFG(T o) {
obj = o;
}
T getob() {
return obj;
}
}
After Type Erasure
class GFG{
Object obj;
GFG(Object o){
obj = o;
}
Object getob(){
return obj;
}
}
Since T has no explicit bound, it is replaced with Object.
2. Bounded Type Example
class Geeks<T extends String>{
T str;
Geeks(T o) {
str = o;
}
T getob() {
return str;
}
}
After Type Erasure
class Geeks{
String str;
Geeks(String o){
str = o;
}
String getob(){
return str;
}
}
Here, T is bounded by String, so it is replaced with String.
Viewing Erased Bytecode
After compiling the classes, you can inspect their contents using:
javap GFG
javap Geeks
These will show that the type parameters no longer exist at runtime.
Type Erasure in Action
Now, we will see example to understand how type erasure affects code during runtime.
Example 1: Using Generics
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Geeks{
public static void main(String args[]){
List<String> list = new ArrayList<>();
list.add("Hello");
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String s = iter.next();
System.out.println(s);
}
}
}
Output
Hello
Explanation:
- Compiler checks type safety -> OK (String list)
- At runtime, the generic type (String) does not exist.
- Type casts inserted by compiler ensure safety.
Example 2: Raw Types
class Box<T extends Number> {
private T value;
Box(T value) { this.value = value; }
T getValue() { return value; }
}
public class Geeks {
public static void main(String[] args)
{
Box<Integer> intBox = new Box<>(10);
Box<Double> doubleBox = new Box<>(5.5);
System.out.println(intBox.getValue());
System.out.println(doubleBox.getValue());
}
}
Output
10 5.5
Explanation: T extends Number restricts T to Number and its subclasses. Therefore, types such as Integer and Double can be used, making the purpose of a bounded type parameter clear.
Issues Caused by Type Erasure
The issues caused by Type Erasure is listed below:
- The JVM loses information of the generic type at runtime, that's why we can not use instanceof or create new instances of generic types.
- When we work with raw types, the compiler will give warning.
- We can not create array of generic types directly.