Generics
Wildcards & PECS
Generic types are invariant — a
List<Manager> is not a List<Employee>. Wildcards restore flexibility at API boundaries: ? extends T for inputs you read from (producers), ? super T for outputs you write to (consumers).- PECS: Producer →
extends, Consumer →super(EJ 31) - From a
List<? extends T>you can only read T's — adds are rejected - Into a
List<? super T>you can only write T's — reads giveObject - Wildcards belong in method signatures, not return types or fields
- Unbounded
<?>: "some specific type, unknown here" — read-only, null-add-only
List<Manager> managers = List.of(boss);
// List<Employee> staff = managers; // compile error: invariant!
List<? extends Employee> staff = managers; // OK: covariant VIEW
Employee e = staff.get(0); // reading produces Employee — safe
// staff.add(new Employee()); // rejected: might be a List<Manager>!The compiler's logic: staff might point at a List<Manager>, so inserting a plain Employee would poison it. Conversely List<? super Manager> might be a List<Object>, so reads only promise Object — but inserting a Manager is always safe. Producers you read from: extends. Consumers you write into: super.
public static <T> void copy(List<? super T> dest, // consumer of T
List<? extends T> src) { // producer of T
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}
// Callers get maximum flexibility:
List<Number> numbers = ...;
List<Integer> ints = ...;
Collections.copy(numbers, ints); // Integer source into Number destination — natural