Question
What is the difference between List<? super T> and List<? extends T> in Java generics?
I previously used List<? extends T>, but Java does not allow me to add an element such as list.add(element) to that list. However, List<? super T> allows adding T values. Why does this happen, and when should each wildcard be used?
Short Answer
By the end of this page, you will understand Java's upper-bounded (? extends T) and lower-bounded (? super T) wildcards. You will know why extends is appropriate for reading T values, why super is appropriate for adding T values, and how the PECS rule helps you choose between them.
Concept
A wildcard describes an unknown type in a generic collection.
List<? extends T>means: “a list whose element type is some unknown subtype ofT.”List<? super T>means: “a list whose element type is some unknown supertype ofT.”
Assume this class hierarchy:
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
A List<? extends Animal> may actually be a List<Dog>, List<Cat>, or List<Animal>.
Because it might be a List<Dog>, Java cannot safely let you add an arbitrary Animal:
List<? extends Animal> animals = new ArrayList<Dog>();
// animals.add(new Cat()); // Would be unsafe if the real list is List<Dog>
However, every item read from that list is guaranteed to be at least an Animal, so reading is safe.
A List<? super Dog> may actually be a List<Dog>, List<Animal>, or List<Object>.
Each of those lists can safely store a Dog, so adding a Dog is safe. But when reading, Java can guarantee only that the value is an Object, because the actual list might be a List<Object>.
This is summarized by PECS:
- Producer Extends: use
? extends Twhen a structure produces or suppliesTvalues to your code. - Consumer Super: use
? super Twhen a structure consumes or receivesTvalues from your code.
The rule is about the direction values move through a method parameter, not whether the collection itself is inherently read-only or write-only.
Mental Model
Imagine a List<? extends Animal> as a sealed box labeled “contains a kind of animal.” It could contain only dogs, only cats, or mixed animals. You may take out an Animal, because every possible box contains animals. But you cannot put a cat in: the box could be a dogs-only box.
Imagine a List<? super Dog> as a container guaranteed to accept dogs. It might be a dog container, an animal container, or an object container. You can place a dog into all of them. When taking something out, though, the only label you can trust is Object.
Syntax and Examples
Core syntax
List<? extends T> source;
List<? super T> destination;
For a concrete example using Animal and Dog:
import java.util.ArrayList;
import java.util.List;
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
public class WildcardExample {
public static void main(String[] args) {
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
List<? extends Animal> animalProducer = dogs;
Animal animal = animalProducer.get(0); // Safe: every element is an Animal
// animalProducer.add(new Dog()); // Not allowed
List<Animal> animals = new ArrayList<>();
List<? super Dog> dogConsumer = animals;
dogConsumer.add(new Dog()); // Safe
Object value = dogConsumer.get(0); // Only Object is guaranteed
}
}
animalProducer might refer to List<Dog>, so adding even a Dog is rejected: Java does not know the list's exact element type through that reference.
dogConsumer can refer to a List<Dog>, List<Animal>, or List<Object>. All of those can accept a Dog.
Step by Step Execution
Consider a method that copies dogs into a compatible destination:
import java.util.List;
class Animal {}
class Dog extends Animal {}
static void copyDogs(List<? extends Dog> source, List<? super Dog> destination) {
for (Dog dog : source) {
destination.add(dog);
}
}
Step by step:
sourcecan be aList<Dog>or a list of a subclass ofDog.- When the loop reads an item from
source, Java guarantees that it is aDog, soDog dogis valid. destinationcan be aList<Dog>,List<Animal>, orList<Object>.- Every valid destination can store a
Dog, sodestination.add(dog)is valid. - The method can safely be called with
List<Dog>as the source andList<Animal>as the destination.
List<Dog> dogs = List.of(new Dog(), new Dog());
List<Animal> animals = new ArrayList<>();
copyDogs(dogs, animals);
The source produces dogs for the method; the destination consumes dogs from the method.
Real World Use Cases
- Copying data: Use
? extends Tfor a source collection and? super Tfor a destination collection. - Sorting: Java's
Collections.sortaccepts comparators using lower bounds because a comparator of a broader type can compare narrower values. - Event handlers: A method that registers handlers for
Dogevents can often accept a handler forAnimalevents. The handler consumes dogs, so asuperbound is useful. - Numeric calculations: A method that reads numbers without modifying the input may accept
List<? extends Number>, allowingList<Integer>,List<Double>, and similar lists. - Bulk insertion: A method that inserts
Doginstances can acceptCollection<? super Dog>, allowing callers to provide collections of dogs, animals, or objects.
Real Codebase Usage
Developers usually place wildcards on method parameters, where they make APIs more flexible without weakening type safety.
Read from a flexible source
static double sum(List<? extends Number> numbers) {
double total = 0;
for (Number number : numbers) {
total += number.doubleValue();
}
return total;
}
This method works with List<Integer>, List<Long>, and List<Double> because it only reads Number values.
Write to a flexible destination
static void addDefaultDogs(List<? super Dog> destination) {
destination.add(new Dog());
destination.add(new Dog());
}
This works with List<Dog>, List<Animal>, and List<Object>.
Common API pattern: copy
static <T> void copy(List<? extends T> source, List<? super T> destination) {
for (T item : source) {
destination.add(item);
}
}
Use a plain List<T> when the method must both read and add values as exactly T, or when callers should provide exactly that type. Use wildcards only when variance is needed.
Common Mistakes
Assuming extends means the list is immutable
List<? extends T> is not necessarily immutable. The original list may be mutable, but this particular wildcard reference cannot safely add non-null elements.
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
// animals.add(new Dog()); // Compile-time error
You may still remove through the reference if the underlying list supports removal, and you may add null because null fits every reference type. In practice, avoid relying on null insertion.
Expecting to read T from ? super T
This does not compile:
List<? super Dog> dogs = new ArrayList<Animal>();
// Dog dog = dogs.get(0); // Compile-time error
The actual list might be List<Object>, whose elements are not necessarily dogs. Read it as Object instead.
Object value = dogs.get(0);
Thinking ? extends Animal accepts only List<Animal>
It also accepts lists of subclasses:
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
That flexibility is the main reason to use an upper bound.
Thinking ? super Dog accepts List<Cat>
It does not. Cat is not a supertype of Dog.
List<Cat> cats = new ArrayList<>();
// List<? super Dog> destination = cats; // Compile-time error
Using wildcards on fields without a need
Wildcard fields can make later use awkward. Prefer a concrete generic type for stored state when possible, and use bounded wildcards primarily in method parameters.
Comparisons
| Declaration | Possible actual list types | Safely read as | Safely add |
|---|---|---|---|
List<T> | Only List<T> | T | T |
List<? extends T> | List<T> or a list of a subtype of T | T | Nothing except null |
List<? super T> | List<T> or a list of a supertype of T | Object | T and subclasses of T |
List<?> | A list of any reference type | Object | Nothing except null |
extends is an upper bound: the unknown element type is T or below it in the inheritance hierarchy.
super is a lower bound: the unknown element type is T or above it in the inheritance hierarchy.
For example, if Dog extends Animal:
List<? extends Animal> a = new ArrayList<Dog>(); // Valid
List<? super Dog> b = new ArrayList<Animal>(); // Valid
Neither wildcard changes the objects in the list. It changes what the compiler can prove is safe through that reference.
Cheat Sheet
// Read T values from a source
List<? extends T> source;
T item = source.get(0);
// Add T values to a destination
List<? super T> destination;
destination.add(item);
// Copy pattern
static <T> void copy(List<? extends T> source, List<? super T> destination) {
for (T item : source) {
destination.add(item);
}
}
- Use
? extends Twhen the parameter producesTvalues. - Use
? super Twhen the parameter consumesTvalues. - From
? extends T, read asT; do not add non-nullvalues. - To
? super T, addTor subclasses; read only asObject. - Remember: PECS = Producer Extends, Consumer Super.
- Prefer
List<T>when you need both reliable reads asTand writes ofTthrough the same reference.
FAQ
Why can't I add a T to List<? extends T>?
The list might actually be a List<SubclassOfT>. Adding a plain T could violate that list's element type.
Can I add a subclass of T to List<? extends T>?
No. The unknown list type could be a different subclass. For example, a List<? extends Animal> could be a List<Cat>, so adding a Dog would be unsafe.
Why can I add T to List<? super T>?
The actual list type is guaranteed to be T or one of its supertypes. All such lists can store a T.
Why does get() return Object from List<? super T>?
The actual list could be List<Object>, which may contain values that are not T. Therefore, Object is the only guaranteed type.
Can List<? super T> accept subclasses of T?
Yes. If a list can store T, it can also store any subtype of T.
Does ? extends T mean inheritance only?
It is based on Java's subtype relationship. It includes T itself as well as its subclasses and implementing types where applicable.
Should I always use PECS?
Use it when a method needs to accept collections with compatible element types. If exact typing is clearer and sufficient, use List<T> instead.
Mini Project
Description
Build a small utility that copies records from a source list into a destination list. It demonstrates the PECS pattern used by many collection APIs: a source produces values and a destination consumes them.
Goal
Create a type-safe method that copies Dog values from compatible source lists into compatible destination lists.
Requirements
- Create
Animal,Dog, andPoodleclasses, whereDogextendsAnimalandPoodleextendsDog. - Write a
copyDogsmethod that accepts a source list and a destination list. - Allow a
List<Poodle>to be used as the source. - Allow a
List<Animal>and aList<Object>to be used as destinations. - Copy every source item into each destination and print the resulting sizes.
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.