What is the difference between extends T> and super T>?
Upper bound: type T or any of its subtypes.
π’ Junior Level
<? extends T> β covariance (Producer).
- You can read elements as type
T. - Cannot write (except
null).
List<? extends Number> numbers = List.of(1, 2, 3);
Number n = numbers.get(0); // β
can read
// numbers.add(4); // β cannot write
<? super T> β contravariance (Consumer).
- You can write elements of type
T. - Can only read as
Object.
List<? super Integer> integers = new ArrayList<>();
integers.add(42); // β
can write
// Integer i = integers.get(0); // β can only read as Object
Simple rule (PECS):
- Producer Extends β reading data β
? extends T - Consumer Super β writing data β
? super T
π‘ Middle Level
Covariance β <? extends T>
Upper bound: type T or any of its subtypes.
List<? extends Number> nums = new ArrayList<Integer>();
Number n = nums.get(0); // β
Integer is a Number
// nums.add(42); // β compiler doesn't know if it's Integer or Double
Why you cannot write: List<? extends Number> could be a List<Integer> or List<Double>. If writing Double were allowed but the actual list is List<Integer> β integrity is broken.
Contravariance β <? super T>
Lower bound: type T or any of its supertypes.
List<? super Integer> target = new ArrayList<Number>();
target.add(42); // β
Integer fits into Number or Object
Object o = target.get(0); // β
can only read as Object
Why you cannot read typed values: List<? super Integer> could be List<Number> or List<Object>. There is no guarantee that Integer elements are stored there.
Comparison
| Characteristic | <? extends T> |
<? super T> |
|---|---|---|
| Role | Producer (source) | Consumer (sink) |
| Read | β
As T |
β Only as Object |
| Write | β Forbidden | β
As T |
| Hierarchy | T and all below |
T and all above |
Capture of Wildcard
Common mistake:
void reverse(List<?> list) {
// list.set(0, list.get(0)); // ERROR: capture of ?
}
The compiler cannot match the type you βgotβ with the type for writing. Solution: A helper generic method that βcapturesβ the type:
void reverse(List<?> list) { reverseHelper(list); }
private <T> void reverseHelper(List<T> list) {
// now T is known
}
π΄ Senior Level
Use-site variance
Java uses variance at the use site β in method parameters.
This differs from Kotlin/Scala, which have declaration-site variance (out T / in T).
Wildcards do NOT prevent allocations
Using wildcards in method signatures does not prevent allocations.
ArrayList<Integer> allocates Integer objects regardless of whether
it is passed as List<? extends Number>.
Wildcards are only compile-time type checking.
Unbounded Wildcard <?>
This is effectively <? extends Object>. Useful when the code only uses Object methods:
void printSize(List<?> list) {
System.out.println(list.size()); // only Object methods
}
Best Practices
// β
extends β for extracting data (Producer)
public double sum(List<? extends Number> numbers) { ... }
// β
super β for accumulating data (Consumer)
public void fill(List<? super Integer> list, int value) { ... }
// β
No wildcard when you need both read and write
public <T> void swap(List<T> list, int i, int j) { ... }
// β Extends for writing
// β Super for reading a specific type
π― Interview Cheat Sheet
Must know:
<? extends T>β covariance (Producer): can read as T, cannot write (except null)<? super T>β contravariance (Consumer): can write T, can only read as ObjectList<String>does NOT inherit fromList<Object>β generics are invariant- PECS rule: Producer Extends, Consumer Super
- Wildcards β use-site variance (unlike Kotlinβs declaration-site variance)
<?>=<? extends Object>β for cases when only Object methods are needed
Frequent follow-up questions:
- Why does <? extends T> forbid writing? β The compiler doesnβt know the actual type (could be List
), writing would break integrity - What can you write into List<? super Integer>? β Integer and its subclasses, because they fit into any supertype
- What is capture of wildcard? β The compiler cannot link get() with set() for
?, a generic helper method is needed - When is wildcard NOT needed? β When you need both read and write β use
<T>
Red flags (DO NOT say):
- β βList<? extends Number> could be List
" β Yes, but you cannot add Integer to it - β β<? super T allows reading as Tβ β You can only read as Object
- β βWildcards prevent allocationsβ β This is only a compile-time check
- β βList<?> is a read-only listβ β The compiler blocks writes due to unknown type, not immutable
Related topics:
- [[11. What are Generics in Java]]
- [[13. What is type erasure]]
- [[17. What is PECS (Producer Extends Consumer Super)]]
- [[21. What is the difference between List<?> and List
- [[24. How do Generics work with inheritance]]