Skip to main content

Type Erasure

One of the most important concepts to understand about Java Generics is that Generics do not exist at runtime.

To ensure that Java programs written with generics are backward compatible with older versions of Java (Java 4 and earlier), the Java compiler implements generics using a process called Type Erasure.


What is Type Erasure?

When you compile a Java class that uses generics, the compiler performs the necessary type checks (to ensure Type Safety) and then erases all generic type information from the source code before generating the .class (bytecode) file.

This means that at runtime, the JVM has absolutely no idea that generics were ever used!

How the Compiler applies Type Erasure:

  1. Replaces Type Parameters: It replaces all type parameters in generic types with their bounds.
    • If the type parameter is unbounded (<T>), it is replaced with Object.
    • If the type parameter is bounded (<T extends Number>), it is replaced with the bound (Number).
  2. Inserts Casts: It inserts type casts where necessary to preserve type safety when retrieving objects from collections.
  3. Generates Bridge Methods: It generates synthetic bridge methods to preserve polymorphism in extended generic types.

Example: Code Before & After Compilation

To truly understand this, let's look at what you write in your source code versus what the Java Compiler actually generates in the bytecode.

1. The Source Code (What you write)

public class Box<T> {

private T value;

public void set(T value) {
this.value = value;
}

public T get() {
return value;
}
}

public class Main {

public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String text = stringBox.get();
}
}

2. The Bytecode Equivalent (What the compiler generates)

Because the type <T> in the Box class is unbounded, the compiler replaces it with Object. Furthermore, when retrieving the value in the Main class, the compiler automatically inserts a cast to String.

// The generic type <T> is completely erased!
public class Box {

private Object value;

public void set(Object value) {
this.value = value;
}

public Object get() {
return value;
}
}

public class Main {

public static void main(String[] args) {
// The Box is instantiated just like legacy Java 1.4 code
Box stringBox = new Box();

stringBox.set("Hello");

// The compiler silently inserts the (String) cast for you!
String text = (String) stringBox.get();
}
}

The Consequences of Type Erasure

Because generic type information is erased at runtime, there are certain things you cannot do with generics in Java:

  1. You cannot instantiate generic types with primitive types. (You must use List<Integer>, not List<int>).
  2. You cannot create instances of type parameters. (e.g., new T() is illegal because at runtime, T is just an Object, and the JVM doesn't know what specific object to instantiate).
  3. You cannot use instanceof with parameterized types. (e.g., if (list instanceof ArrayList<String>) is illegal. You can only check if (list instanceof ArrayList<?> )).
  4. You cannot create arrays of parameterized types. (e.g., List<String>[] array = new List<String>[10] is illegal).