Wildcards: ? super (Lower Bounds)
Wildcards: ? super (Lower Bounds)
In the previous lesson you saw ? extends T, which lets you read from a collection safely. This lesson covers the mirror image: ? super T, the lower-bounded wildcard. Where ? extends is for producers (things that give you data), ? super is for consumers (things that accept data you write into them). Together they form the PECS principle — one of the most practically useful rules in Java generics.
The Problem: Writing Into a Generic Collection
Suppose you want to write a method that fills a list with integers generated by a counter. Your first instinct might be:
That compiles, but it is too restrictive. You cannot pass a List<Object> to it, even though an Object list can obviously hold integers. Nor can you pass a List<Number> when the caller happens to have a List<Object>. The fix is a lower bound:
Now the method accepts List<Integer>, List<Number>, List<Object> — any list whose element type is Integer or any ancestor of Integer in the type hierarchy.
Why Writes Are Safe With ? super
The compiler's reasoning: if the list holds some type that is a supertype of Integer, then putting an Integer into it is always valid — because an Integer IS-A Number IS-A Object. The type system guarantees the assignment is safe regardless of which concrete supertype the list actually uses.
List<? super Integer>, all the compiler knows is that each element is some unknown supertype of Integer — it could be Number, Object, or anything else. The only safe read type is Object, which loses all useful information. That is why lower-bounded wildcards are paired with write (consume) operations, not read (produce) operations.
A Concrete, Runnable Example
PECS: Producer Extends, Consumer Super
The acronym PECS (coined by Joshua Bloch in Effective Java) gives you a simple rule to decide which wildcard to use:
- If a parameter produces values that you will read out of it, use
? extends T. - If a parameter consumes values that you will write into it, use
? super T. - If you both read and write, use the exact type
Twith no wildcard.
A classic example that combines both wildcards in one method — copying elements from a source list into a destination list:
Read the signature aloud: "copy from a list that produces Ts (or subtypes) into a list that consumes Ts (or supertypes)." That is exactly what PECS says.
Real-World Usage in the JDK
The JDK itself applies PECS throughout java.util. Look at Collections.sort:
The bound Comparable<? super T> means the comparator only needs to be defined on some ancestor of T — it does not have to be re-declared on every subclass. For example, if Dog extends Animal and Animal implements Comparable<Animal>, you can still sort a List<Dog> because Animal is a supertype of Dog and already provides compareTo.
Common Mistakes
- Reading with ? super and expecting a specific type: The only guaranteed return type is
Object. If you need the specific type, you will need a cast (which defeats type safety) or a different design. - Using ? super when the type parameter appears on both sides: If you read and write through the same parameter, use a plain type variable, not a wildcard.
- Confusing lower bound with lower limit:
? super Integermeans Integer or higher up the hierarchy (Number, Object) — not a smaller/more specific type. The word "super" refers to supertypes.
List<? super Number> you can add a Double (since Double extends Number) but you cannot safely add an arbitrary Object — the compiler will reject it because Object is not guaranteed to be a Number.
Summary
Lower-bounded wildcards (? super T) make a method accept any list whose element type is T or an ancestor of T, enabling safe writes. Remember PECS: use ? extends when a collection is a producer (you read from it) and ? super when it is a consumer (you write into it). The JDK's own collection utilities are built on exactly this principle.