What is the difference between List<?>, List<? extends T>, and List<? super T>?

9 minadvancedwildcardsgenerics

Quick Answer

List<?> is an unbounded wildcard — a list of some unknown type, from which you can only safely read as Object. List<? extends T> is an upper-bounded wildcard — a list of T or some subtype; you can read T values out of it, but can't add to it (the compiler can't guarantee what specific subtype it actually holds). List<? super T> is a lower-bounded wildcard — a list of T or some supertype; you can safely add T values into it, but reading only guarantees Object.

Detailed Answer

Wildcards express "some unknown type" with varying degrees of constraint, used mainly for method parameters that need flexibility beyond an exact generic type match:

  • List<?> (unbounded wildcard): a list of some type, but the compiler doesn't know which. You can call type-agnostic methods (size(), clear()), and read elements only as Object; you cannot add() anything except null (the compiler can't verify any specific type is safe to insert).
void printAll(List<?> list) {
    for (Object o : list) System.out.println(o); // fine — read as Object
}
  • List<? extends T> (upper-bounded / "producer"): a list of T or any subtype of T. Safe to read T (or a supertype) out of it, since every element is guaranteed to be at least a T. Cannot add (except null), because the compiler doesn't know the exact subtype — adding a plain T could violate an actual List<Dog> masquerading as List<? extends Animal>.
double sum(List<? extends Number> nums) {
    double s = 0;
    for (Number n : nums) s += n.doubleValue(); // read OK
    // nums.add(5); // compile error
    return s;
}
  • List<? super T> (lower-bounded / "consumer"): a list of T or any supertype of T. Safe to add a T (or subtype) into it, since any supertype list can hold a T. Reading only guarantees Object, since the actual list could be any supertype.
void addNumbers(List<? super Integer> list) {
    list.add(1); list.add(2); // write OK
    Object o = list.get(0);   // only guaranteed to be Object
}

This read/write asymmetry is exactly the PECS principle: "Producer extends, Consumer super."