Generic Wildcards in Java β€” <code>?</code>, <code>? extends</code>, <code>? super</code>

A wildcard (?) stands for "some unknown type" in a generic parameter. It exists because generics are invariant by default: List<Integer> is not a subtype of List<Number>. Wildcards let you write methods that accept a family of parameterised types.

Unbounded ?

public static void printAll(List<?> list) {
    for (Object o : list) System.out.println(o);
}

printAll(List.of(1, 2, 3));
printAll(List.of("a", "b"));

You can read Object from a List<?>, but you can't add anything (except null) β€” the compiler doesn't know what type is allowed.

Upper-bounded β€” ? extends T

public static double sum(List<? extends Number> nums) {
    double total = 0;
    for (Number n : nums) total += n.doubleValue();
    return total;
}

sum(List.<Integer>of(1, 2, 3));
sum(List.<Double>of(1.0, 2.0));

You can read as Number. You can't add β€” the list might be a List<Integer> and accept no Doubles.

Lower-bounded β€” ? super T

public static void addIntegers(List<? super Integer> dst) {
    dst.add(1);
    dst.add(2);
}

addIntegers(new ArrayList<Number>());
addIntegers(new ArrayList<Object>());

You can add Integer (or its subtypes). You can only read as Object.

PECS β€” Producer Extends, Consumer Super

The mnemonic for when to use which:

  • If the parameter produces values (you read from it), use ? extends T.
  • If the parameter consumes values (you write to it), use ? super T.
  • If you do both, use T.
public static <T> void copy(List<? extends T> src, List<? super T> dst) {
    for (T t : src) dst.add(t);
}

Common mistakes

  • Writing List<Object> when you mean List<?> β€” the first doesn't accept List<String>, the second does.
  • Trying to add to ? extends β€” the compiler refuses; you don't know the exact type.
  • Over-using wildcards β€” for a single type parameter, <T> on the method is often cleaner than ? extends T.

Related

Pillar: Java generics. See also bounded types, erasure.