Memra

Wildcards: ? extends / ? super (PECS)

Producer-extends, consumer-super — covariance and contravariance for generic parameters.

Generics are invariant

The surprising rule first: List<Integer> is not a subtype of List<Number>, even though Integer is a subtype of Number. Generic types are invariant. The reason is safety — if it were allowed:

List<Integer> ints = new ArrayList<>();
List<Number> nums = ints;   // NOT allowed (pretend it were)
nums.add(3.14);             // a Double into a List<Integer>?! would corrupt ints

To regain flexibility *safely*, use wildcards — the ? type, optionally bounded.

? extends — an upper bound (a producer)

List<? extends Number> means 'a list of some unknown type that is Number or a subtype'. You can pass a List<Integer> or List<Double> to it. Because every element IS-A Number, you can safely read elements as Number:

static double sum(List<? extends Number> list) {
    double total = 0;
    for (Number n : list) {   // safe: each element is at least a Number
        total += n.doubleValue();
    }
    return total;
}
sum(List.of(1, 2, 3));        // List<Integer> — accepted
sum(List.of(1.5, 2.5));       // List<Double>  — accepted

But you cannot add to a List<? extends Number> (except null): the compiler does not know the exact element type, so it cannot verify that what you add is legal. ? extends is a producer — you take values out.

? super — a lower bound (a consumer)

List<? super Integer> means 'a list of some unknown type that is Integer or a *supertype*'. You can pass a List<Integer>, List<Number>, or List<Object>. Because the list accepts Integer or anything broader, you can safely add Integers:

static void addInts(List<? super Integer> dst) {
    dst.add(1);              // safe: dst holds Integer or a supertype
    dst.add(2);
}
addInts(new ArrayList<Number>());   // accepted
addInts(new ArrayList<Object>());   // accepted

But reading back only gives you Object (the one type the unknown supertype is guaranteed to be). ? super is a consumer — you put values in.

PECS: Producer Extends, Consumer Super

The rule, from Joshua Bloch: when a parameter both produces and consumes you would pick based on its role. A copy method needs both:

static <T> void copy(List<? super T> dst, List<? extends T> src) {
    for (T item : src) {   // src PRODUCES T  → extends
        dst.add(item);     // dst CONSUMES T  → super
    }
}

src produces (extends), dst consumes (super). This signature lets you copy a List<Integer> into a List<Number> — maximally flexible and still type-safe.

NORMAL ~/memra/learn/comp-308/wildcards-pecs utf-8 LF