? extends vs ? super
TL;DR
? extends Tand? super Tare Wildcard Bounds in Java Generics.? extends T(Upper Bound): The collection can holdTor any of its children. It is Read-Only. (You can get items out, but you can’t put items in).? super T(Lower Bound): The collection can holdTor any of its parents. It is Write-Only. (You can put items in, but getting them out is annoying).- Rule of Thumb (PECS): Producer
Extends, ConsumerSuper.
Concept
Generics are strict. A List<Dog> is NOT a List<Animal>. If a method takes List<Animal>, you literally cannot pass a List<Dog> into it. This is to prevent you from putting a Cat into what is secretly a List<Dog>.
To fix this rigidity, we use wildcards.
If a method just wants to read animals (a Producer of data), it accepts List<? extends Animal>. Now you can pass a List<Dog> into it. The method can safely read items (it knows whatever comes out is at least an Animal), but Java prevents the method from adding anything to the list, guaranteeing type safety.
If a method wants to add Dogs to a list (a Consumer of data), it accepts List<? super Dog>. You can pass a List<Animal> or List<Object> into it. The method can safely add a Dog to those lists, but if it tries to read from the list, Java will only guarantee that the item is an Object.
Examples
import java.util.*;
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
public class WildcardDemo {
// PRODUCER EXTENDS (PE) - Read Only
// Accepts List<Animal>, List<Dog>, List<Cat>
public static void readAnimals(List<? extends Animal> list) {
// ✅ We can READ perfectly safely. We know it's an Animal.
for (Animal a : list) {
System.out.println(a);
}
// ❌ COMPILE ERROR: Java forbids adding to 'extends'.
// What if the list passed in was secretly a List<Cat>?
// Adding a Dog would crash the program!
// list.add(new Dog());
}
// CONSUMER SUPER (CS) - Write Only
// Accepts List<Dog>, List<Animal>, List<Object>
public static void addDogs(List<? super Dog> list) {
// ✅ We can WRITE safely. A List<Animal> can certainly hold a Dog.
list.add(new Dog());
// ❌ We can technically read, but Java forces it to be an 'Object'.
// Because if the list is List<Object>, pulling it out as a Dog would crash!
Object obj = list.get(0);
}
}
Interview Questions
Q: What does the acronym PECS mean?
A: It stands for Producer Extends, Consumer Super. It was coined by Joshua Bloch in Effective Java.
- If your method needs a collection to produce data for you to read, declare it with
? extends T. - If your method needs a collection to consume data that you generate and write into it, declare it with
? super T. - If your method needs to do both (read and write to the exact same list), do not use wildcards; use the exact type
List<T>.
Q: Why does Java prevent you from adding to a ? extends list?
A: Imagine you have List<Cat> cats = new ArrayList<>().
If a method accepted List<? extends Animal> animals, you could pass your cats list into it.
If Java allowed that method to call animals.add(new Dog()), the code would happily put a Dog into your cats list. Later, when you loop through cats expecting only Cats, you will encounter a Dog, throwing a ClassCastException. To prevent this, the compiler entirely bans the .add() method on ? extends bounds.