Writing Your Own Generic Types
Type parameters on your own classes and methods, bounded types, and what erasure will not let you do.
Open this lesson in the learning hubKey points
- A type parameter is a hole the caller fills:
Box<T>takes a T and hands a T back, with no cast anywhere. - A generic method declares its own parameter before the return type:
static <T> T largest(List<T> items). - Bound it when the body needs behaviour:
<T extends Comparable<T>>is what allowscompareTo. - Generics are compile-time only. After erasure a
Box<String>and aBox<Integer>are the same class. - So
new T(),new T[10]andinstanceof Box<String>are all illegal. Pass in a factory. - Generic types are invariant: a
List<String>is not aList<Object>, which is why wildcards exist.
Example
import java.util.List;
import java.util.function.Function;
public class Main {
// One type parameter turns one class into a family of type-safe classes.
static final class Box<T> {
private final T value;
private Box(T value) { this.value = value; }
static <T> Box<T> of(T value) { return new Box<>(value); }
T get() { return value; } // no cast at the call site
<R> Box<R> map(Function<T, R> f) { return Box.of(f.apply(value)); }
@Override public String toString() { return "Box[" + value + "]"; }
}
// Bounded: the body may call compareTo because T is known to have it.
static <T extends Comparable<T>> T largest(List<T> items) {
T best = items.get(0);
for (T item : items) {
if (item.compareTo(best) > 0) best = item;
}
return best;
}
public static void main(String[] args) {
Box<String> name = Box.of("ada");
String upper = name.get().toUpperCase(); // typed, so no cast
System.out.println(name + " -> " + upper);
System.out.println(name.map(String::length));
System.out.println("largest int = " + largest(List.of(3, 11, 7)));
System.out.println("largest word = " + largest(List.of("pear", "apple", "fig")));
// Erasure: the type argument is gone by the time the JVM runs this.
System.out.println("same class? = " + (Box.of(1).getClass() == Box.of("x").getClass()));
}
}
Parameterise when only the type changes, and bound it when the body needs behaviour.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the OOP course, and every lesson in it is listed on the OOP contents page.