Java - Generics and Type Erasure in Java

1. What are Generics?

Generics allow you to define classes, interfaces, and methods with a placeholder for the data type. This enables compile-time type safety and eliminates explicit type casting.

Without generics:

List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);   // explicit casting required

With generics:

List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);   // no casting needed

Here, String is the type parameter.


2. Generic Class

class Box<T> {
    private T value;

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

    public T get() {
        return value;
    }
}

Usage:

Box<Integer> box = new Box<>();
box.set(10);
Integer num = box.get();

T can be replaced with any reference type.


3. Generic Methods

Generics can also be used in methods:

public static <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.println(element);
    }
}

<T> before return type declares the type parameter.


4. Bounded Type Parameters

You can restrict the type:

class Calculator<T extends Number> {
    T num;
}

Now T must be a subclass of Number (Integer, Double, etc.).

Multiple bounds:

<T extends Number & Comparable<T>>

5. Wildcards

Used mainly in collections.

Unbounded:

List<?> list;

Upper bounded:

List<? extends Number>

Lower bounded:

List<? super Integer>

Rule:

  • extends → read-only (producer)

  • super → write-safe (consumer)

This follows the PECS principle:
Producer Extends, Consumer Super.


6. Type Erasure

Java generics are implemented using type erasure.

At runtime, generic type information is removed. The compiler replaces type parameters with:

  • Their bound type (if bounded)

  • Object (if unbounded)

Example:

List<String> list = new ArrayList<>();

After compilation (conceptually):

List list = new ArrayList();

Because of type erasure:

  • You cannot create generic arrays:

    new T[10];  // not allowed
    
  • You cannot use primitives as type parameters:

    List<int>  // not allowed
    List<Integer>  // correct
    
  • Runtime type checking does not know generic types:

    list instanceof List<String>  // not allowed
    

7. Why Type Erasure?

Java maintains backward compatibility with older versions (before Java 5). The JVM does not store generic type information.


8. Advantages of Generics

  1. Compile-time type safety

  2. No need for casting

  3. Code reusability

  4. Better readability

  5. Fewer runtime errors


9. Common Interview Questions

  • Difference between List<?> and List<Object>

  • What is PECS rule?

  • Why can’t we use primitives in generics?

  • What is type erasure?

  • Why can’t we create generic arrays?


In short:

Generics improve type safety and reusability at compile time, while type erasure removes generic information at runtime to maintain backward compatibility.