Generics

Generic Classes & Methods

Declare type parameters on classes (class Box<T>) or individual methods (static <T> T pick(...)). The compiler infers method type arguments from the call — you rarely spell them out.
  • Class-level parameter: class Pair<T> — one type variable for the whole instance
  • Method-level parameter: <T> before the return type; independent per call
  • Type arguments are inferred: Pair.of("a", 1) figures out Pair<String,Integer>
  • Conventions: T type, E element, K/V key/value, R result
  • Favor generic types and methods over Object-based ones (EJ 29–30)
A generic class
public class Pair<T> {
    private final T first;
    private final T second;

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() { return first; }
    public T getSecond() { return second; }
}
A generic method — type parameter belongs to the method
public static <T> T getMiddle(T... a) {
    return a[a.length / 2];
}

String mid = getMiddle("John", "Q.", "Public");   // T inferred as String

Inference occasionally surprises: getMiddle("Hello", 0, null) infers the common supertype of String and Integer — a legal but unintended Object-ish type. When inference goes sideways, pass explicit type arguments: Collections.<String>emptyList().

Generic records work too: record Pair<T>(T first, T second) {} — one line replaces the class above (see Records). Generic constructors infer through the diamond: new HashMap<String, List<Integer>>() is just new HashMap<>() in assignment context.

Sources
  • Core Java, Volume I: Fundamentals (13th ed.)Ch. 8.2–8.3 — Generic Classes; Generic Methods
  • Effective Java (3rd ed.)Items 29, 30