Search⌘ K
AI Features

Generic Classes

Explore how generic classes in Java enforce type safety and enable reusable, flexible code. Understand generic syntax, multiple type parameters, generic interfaces, and Java's type erasure mechanism to write safer, cleaner applications.

We have reached a turning point in how we design Java applications. Up until now, if we wanted to write a class that could handle any type of data, like a storage box or a list of items, we faced a difficult trade-off. We either wrote specific versions for every type (a StringBox, an IntegerBox, a DoubleBox) or we sacrificed safety by using the Object type, effectively turning off the compiler’s ability to check our work.

Generics solve this dilemma. They allow us to write a single class that works with any data type while strictly enforcing type safety. In this lesson, we will build classes that are both flexible and safe, moving potential bugs from runtime crashes to compile-time alerts.

The problem: Life before generics

Before Java 5, if we wanted to create a reusable class that could hold any object, we had to rely on the Object class. Since every class in Java inherits from Object, we could store anything. However, retrieving that data required us to manually “cast” it back to its original type.

This approach relied entirely on the programmer remembering what was inside the box. If we made a mistake, the code would compile perfectly but crash immediately when run.

Consider this legacy style wrapper:

Java 25
class LegacyBox {
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) {
LegacyBox stringBox = new LegacyBox();
stringBox.set("Hello World");
// We must cast (String) because get() returns Object
String text = (String) stringBox.get();
System.out.println("Value: " + text);
// DANGER: We can accidentally put an Integer in a box expected to hold Strings
LegacyBox numberBox = new LegacyBox();
numberBox.set(100);
// This compiles fine, but CRASHES at runtime with ClassCastException
// We are trying to force an Integer into a String variable
String error = (String) numberBox.get();
}
}
  • Lines 1–11: We define LegacyBox to hold Object, meaning it accepts any data type.

  • Line 19: We accidentally cast an stored Integer to a String. The compiler allows this because get() returns Object, and an Object might be a ...