8. Generics & Type Erasure

Java Generics, Type Erasure Mechanics & The PECS Rule

Deep technical guide to Java Generics, compile-time Type Erasure, bridge methods, Wildcards (? extends vs ? super), and the PECS Rule.

๐Ÿ”Œ What are Java Generics?

Introduced in Java 5, Generics provide strong compile-time type safety for classes, interfaces, and methods, allowing developers to parameterize types without resorting to raw Object casting.

  • Analogy: Universal Adapter Plugs & Compile-Time Label Stickers.
    • Pre-Generics (Raw Object): Storing items inside un-labeled cardboard boxes. You pull an item out and hope it's a stereo ((Stereo) box.get()); if someone put a toaster inside, your app crashes at runtime with a ClassCastException!
    • Generics (List<Stereo>): Applying strict color-coded label stickers onto boxes at the factory. If someone tries to put a toaster into a box labeled <Stereo>, the factory inspector (javac compiler) stops them before the shipment leaves the warehouse!
// Compile-time Type Safety with Generics
List<String> names = new ArrayList<>();
names.add("Alice");
// names.add(42); // โŒ Compile Error! Prevented by javac inspector!

String first = names.get(0); // โœ… No explicit (String) cast needed!

๐Ÿงน Type Erasure Mechanics

Java implemented Generics via Type Erasure to maintain strict backward compatibility with legacy Java 1.4 bytecodes:

[!IMPORTANT] What Type Erasure Does:

  1. Replaces all generic type parameters <T> with Object (or their upper bound T extends Number $\rightarrow$ Number).
  2. Inserts automatic explicit type casts when retrieving values.
  3. Generates synthetic Bridge Methods to preserve dynamic method overriding polymorphism.
// What you write in Source Code:
public class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

// What javac generates in Bytecode (Type Erasure):
public class Box {
    private Object value;
    public void set(Object value) { this.value = value; }
    public Object get() { return value; }
}

๐Ÿท๏ธ Wildcards & The PECS Rule (? extends vs ? super)

Wildcards (?) allow flexible generic parameters. The PECS Rule governs when to use extends vs super:

[!TIP] PECS: Producer Extends, Consumer Super

  • Producer Extends (? extends T): Use when your method reads (produces) data from a collection parameter.
  • Consumer Super (? super T): Use when your method writes (consumes) data into a collection parameter.
                                  The PECS Rule
                                        โ”‚
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ–ผ                                                               โ–ผ
Producer Extends (? extends Number)                             Consumer Super (? super Integer)
Use when READING data FROM collection                           Use when WRITING data INTO collection
List<? extends Number> list = new ArrayList<Integer>();          List<? super Integer> list = new ArrayList<Number>();
Number num = list.get(0); // READ SAFE                          list.add(100); // WRITE SAFE
// list.add(10); โŒ WRITE FORBIDDEN!                             // Integer val = list.get(0); โŒ READ UNCERTAIN!
import java.util.List;
import java.util.ArrayList;

public class PecsDemo {

    // 1. Producer Extends: READS numbers from source list (Producer)
    public static double sumOfList(List<? extends Number> list) {
        double sum = 0.0;
        for (Number n : list) {
            sum += n.doubleValue(); // Safe to READ as Number!
        }
        return sum;
    }

    // 2. Consumer Super: WRITES integers into target list (Consumer)
    public static void addIntegers(List<? super Integer> list) {
        for (int i = 1; i <= 3; i++) {
            list.add(i); // Safe to WRITE Integer!
        }
    }

    public static void main(String[] args) {
        List<Integer> intList = List.of(10, 20, 30);
        System.out.println("Sum: " + sumOfList(intList));

        List<Number> numList = new ArrayList<>();
        addIntegers(numList);
        System.out.println("Added Items: " + numList);
    }
}

โ“ Conceptual Quizzes

Knowledge Check

What is Type Erasure in Java Generics?

Knowledge Check

According to the PECS rule, when should you use '? super T' as a generic wildcard?


๐Ÿ’ป Practice Problems

Problem: Bounded Generic Maximum Element Finder

Write a generic static method public static <T extends Comparable<T>> T findMax(List<T> list) that finds and returns the maximum element in a list of comparable elements.

On this page