Back to course

Type Safety with Generics (<T>)

Java Mastery: From Zero to Professional Developer (50-Lesson Journey)

Lesson 40: Type Safety with Generics (<T>)

Generics were introduced in Java 5 to provide type safety at compile time. They allow a class or method to operate on objects of various types while retaining type checking capabilities.

1. The Problem Generics Solve

Before generics, collections stored objects as the base Object type. This meant the compiler couldn't verify the stored type, leading to potential ClassCastException errors at runtime.

java // Pre-Generics (Bad): List names = new ArrayList(); names.add("Alice"); names.add(10); // Compiler allows this, but it will cause a crash later

String name = (String) names.get(0); // String number = (String) names.get(1); // RUNTIME ERROR: ClassCastException

2. Using Generics

We specify the type inside angle brackets (<Type>) when declaring the collection. The type parameter is often represented by T (Type), E (Element), or K/V (Key/Value).

java // With Generics (Good): List names = new ArrayList<>(); // List can only hold Strings names.add("Bob"); // names.add(10); // COMPILATION ERROR! Type checking prevents the mistake

String name = names.get(0); // No casting required

3. Creating a Generic Class

You can make your own classes generic by adding a type parameter to the class declaration.

java public class Box { // T is a placeholder for the type stored in the box private T item;

public void setItem(T item) {
    this.item = item;
}

public T getItem() {
    return item;
}

}

// Usage: Box integerBox = new Box<>(); integerBox.setItem(123); int value = integerBox.getItem();

Key Concept (Erasure): Java generics are implemented using type erasure. At runtime, the type parameters are removed, and the compiler replaces them with Object or their upper bound. This ensures backward compatibility.