The generics allow parameterizing classes, interfaces, and methods with types checked at compile time.
Generics let you put a “label” on a class to tell the compiler exactly what type of data you are going to handle.
// Code with Generics
ArrayList<String> box = new ArrayList<>(); // The "Diamond" <>
box.add("Hello");
// box.add(123); // COMPILATION ERROR! The IDE underlines it in red.Thanks to generics, we turn runtime errors (which crash the app) into compilation errors (that you fix before delivery).
Creating Your Own Generic Classes
You can not only use generics, you can create your own classes that adapt to any type. We use T (by convention for “Type”) as a placeholder.
// Define a class that handles an as-yet-unknown type "T"
public class Box<T> {
private T content;
public void store(T object) {
this.content = object;
}
public T retrieve() {
return content;
}
}Now we can reuse this logic for anything:
// A box of Strings
Box<String> textBox = new Box<>();
textBox.store("Secret");
// A box of Integers (Reusing the same class)
Box<Integer> numberBox = new Box<>();
numberBox.store(42);Common Naming:
E: Element (used in Collections).K,V: Key, Value (used in Maps).T: Generic Type.
Wildcards ? and Invariance
This is where it usually gets a bit tricky. Pay attention.
We know that String is a child of Object.
So, is List<String> a child of List<Object>?
The answer is NO. In Java, generics are Invariant.
List<String> stringList = new ArrayList<>();
List<Object> objectList = stringList; // COMPILATION ERROR!Why?
If Java allowed this, you could do the following:
- Pass your list of Strings to a
List<Object>variable. - Since it’s a list of objects, add an
Integerto it. - Now your original list of Strings has a number inside. You’ve broken type safety!
The Wildcard ?
Sometimes we need methods that accept “a list of anything”. For that, we use the question mark ?.
// Accepts a list of ANY type, but it will be read-only (limited)
public void printList(List<?> list) {
for (Object o : list) {
System.out.println(o);
}
}Bounded Wildcards (extends)
Sometimes we want to be more specific: “I accept any list, as long as they are Numbers (Integer, Double, Float…)”.
// Upper Bounded Wildcard
public double sumList(List<? extends Number> list) {
double sum = 0;
for (Number n : list) {
sum += n.doubleValue();
}
return sum;
}This is very powerful for creating flexible APIs.
Type Erasure
It’s important to know that generics in Java are a compiler illusion.
Java implements generics primarily through type erasure: the executable bytecode uses Object or the upper bound, and the compiler inserts casts and, when needed, bridge methods. Some generic information is retained as metadata for reflection and tooling.
This is called Type Erasure.
- Consequence: You cannot do
new T()(because at runtime we don’t know what T is). - Consequence: You cannot use primitive types (
List<int>). You have to use their Wrappers (List<Integer>).