Java Generics: Type Safety, Bounded Types, Erasure & Wildcards

Introduced in JDK 5, Generics empower classes, interfaces, and methods to operate on parameterized types. Generics eliminate explicit casting, enforce strict compile-time type checking, and prevent the dreaded ClassCastException.

Before Generics (Dangerous Runtime Crashes)

// Pre-Java 5: Non-generic raw types
List list = new ArrayList();
list.add("Veewom");
list.add(100); // No error at compile time!

// Crashes at runtime with ClassCastException!
String s = (String) list.get(1); // RUNTIME CRASH!

With Generics (Compile-Time Type Safety)

// With Generics: Strict type safety enforced by javac
List<String> list = new ArrayList<>();
list.add("Veewom");
// list.add(100); // COMPILER ERROR! Refuses to compile incompatible types.
String s = list.get(0); // No manual casting required!

1. Creating a Generic Class

public class Box<T> {
    private T content;

    public void set(T item) { this.content = item; }
    public T get() { return this.content; }

    public static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.set("Java 21");
        System.out.println("Box contains: " + stringBox.get());

        Box<Integer> intBox = new Box<>();
        intBox.set(404);
        System.out.println("Integer Box: " + intBox.get());
    }
}

2. Bounded Types & Wildcards (PECS Rule)

To restrict type parameters to specific class families, use Bounded Types (e.g. <T extends Number>):

import java.util.List;

public class WildcardDemo {
    // Upper Bounded Wildcard (? extends Number): Read-Only (Producer)
    public static double sumOfNumbers(List<? extends Number> list) {
        double sum = 0.0;
        for (Number n : list) {
            sum += n.doubleValue();
        }
        return sum;
    }

    // Lower Bounded Wildcard (? super Integer): Write-Only (Consumer)
    public static void addIntegers(List<? super Integer> list) {
        list.add(10);
        list.add(20);
    }
}

University Exam Corner (BCA / B.Sc CS)

Exam Question: "Can primitive types (like int, double) be used directly as generic type arguments (e.g. List<int>)?"

Answer: No! In Java, generic type parameters must be reference types extending java.lang.Object. Because Type Erasure converts type parameters to Object, primitive types cannot be used. You must use their corresponding Wrapper classes: List<Integer>, List<Double>.

Next Step

Now let us explore modern functional programming with Lambda Expressions and Method References. Proceed to: Java Lambda Expressions & '::' →

Frequently Asked Questions (FAQ)

Q1. What is the primary benefit of Generics in Java?
Generics (introduced in JDK 5) provide compile-time type safety. They enable bugs to be caught by the compiler rather than throwing ClassCastException at runtime, while eliminating the need for manual type casting.
Q2. What is Type Erasure in Java?
Type Erasure is the compile-time process where the javac compiler replaces all generic type parameters with their upper bounds (or Object if unbounded) and inserts explicit casts in the generated bytecode. This guarantees backwards compatibility with pre-Java 5 legacy code.
Q3. What is the PECS rule for generic wildcards?
PECS stands for Producer Extends, Consumer Super. If your parameterized collection produces items (you are reading from it), use '? extends T'. If your collection consumes items (you are writing/adding to it), use '? super T'.

Explore Related Java Tutorials

Accelerate your Java mastery by studying these tightly connected core tutorials:

Collections Framework →

Using generics with collections.

Lambda Expressions →

Generic functional interfaces.

Java Stream API →

Type-safe stream transformations.

Java Optional Class →

Generic optional wrappers.

Share Share on Facebook Share on Twitter Share on LinkedIn Pin on Pinterest Share on Stumbleupon Share on Tumblr Share on Reddit Share on Diggit

You may also like this!