JavaWildcards

Wildcards

A wildcard, written ?, represents an unknown type in a generic type argument. Wildcards show up mainly in method parameters, where they let a method accept a wider range of generic types than a fixed type parameter would allow.

Why generics need wildcards: no covariance

Java arrays are covariant — an Object[] reference can point to a String[] array, because String is a subtype of Object. Generics deliberately do not work this way. List<Object> and List<String> have no subtype relationship at all, even though String is a subtype of Object.

Generics are not covariant — this does not compile

Java
Object[] objectArray = new String[3];   // legal — arrays ARE covariant
objectArray[0] = 42;                    // compiles, but throws ArrayStoreException at runtime!

List<Object> list = new ArrayList<String>();  // compile-time error — will not compile
// If it were allowed:
// list.add(42);          // this would silently corrupt a List<String> with an Integer
Note
Array covariance is a known weak spot in Java's type system — it trades compile-time safety for runtime risk (`ArrayStoreException`). Generics were designed to close that hole by rejecting the equivalent assignment at **compile time** instead, which is why `List<Object> list = new ArrayList<String>();` simply will not compile.

This is safe, but it creates a real practical problem: how do you write a method that can accept a List<Integer> or a List<Double> or a List<String>, if none of them are related to each other as generic types? A fixed type parameter like List<Object> will not accept a List<String> argument at all. Wildcards solve exactly this problem.

Upper bounded wildcards: ? extends T

List<? extends Number> means “a list of some unknown type that is Number or a subtype of Number” — it could be a List<Integer>, List<Double>, or List<Number> itself. You can safely read elements out (they are guaranteed to be at least a Number), but you cannot safely add to the list — the compiler cannot know the list's real element type, so adding an Integer to what might actually be a List<Double> could corrupt it. The only value you can ever add is null.

? extends — safe to read, not safe to add

Java
public static double sumAll(List<? extends Number> numbers) {
    double total = 0;
    for (Number n : numbers) {   // reading is safe
        total += n.doubleValue();
    }
    return total;
}

public static void main(String[] args) {
    List<Integer> ints = List.of(1, 2, 3);
    List<Double> doubles = List.of(1.5, 2.5);

    System.out.println(sumAll(ints));    // works
    System.out.println(sumAll(doubles)); // also works — same method!

    // numbers.add(5);  // would not compile inside sumAll — unsafe to add
}
Lower bounded wildcards: ? super T

List<? super Integer> means “a list of some unknown type that is Integer or a supertype of Integer” — it could be List<Integer>, List<Number>, or List<Object>. Here the relationship flips: you can safely add an Integer (or any subtype of it) to the list, because every one of those possible list types is guaranteed to be able to hold an Integer. Reading is less useful — you only know you will get back an Object.

? super — safe to add, limited to read as Object

Java
public static void addNumbers(List<? super Integer> list) {
    list.add(1);   // safe — any of the possible types can hold an Integer
    list.add(2);
    list.add(3);
}

public static void main(String[] args) {
    List<Number> numbers = new ArrayList<>();
    addNumbers(numbers);          // works

    List<Object> objects = new ArrayList<>();
    addNumbers(objects);          // also works — same method!
}
PECS: Producer Extends, Consumer Super

A simple mnemonic for remembering which wildcard to use is PECS — Producer Extends, Consumer Super. If a structure only produces values you read out, use ? extends T. If it only consumes values you put in, use ? super T. This exact pattern is how Collections.copy() is declared in the JDK.

Wildcard

Meaning

Can read?

Can add (besides null)?

? extends T

Unknown subtype of T (producer)

Yes, as T

No

? super T

Unknown supertype of T (consumer)

Only as Object

Yes, T and its subtypes

? (unbounded)

Completely unknown type

Only as Object

No

Warning
Wildcards can only appear in a type *usage* (a variable type, a parameter type, a return type) — you cannot use `?` when declaring a generic class or method's own type parameter, and you cannot create an instance of a wildcard type like `new ArrayList<?>()`.
  • Generics are invariant — List<String> is not a subtype of List<Object>, unlike arrays which are covariant.

  • ? extends T bounds an unknown type to T or its subtypes — safe to read, unsafe to add (a producer).

  • ? super T bounds an unknown type to T or its supertypes — safe to add T, limited read as Object (a consumer).

  • PECS: Producer Extends, Consumer Super — the rule of thumb for choosing between them.