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>();
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
List<Animal> animals = <>();
List<? Dog> dogConsumer = animals;
dogConsumer.add( ());
dogConsumer.get();
}
}
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.
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 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.
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 |
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 as and writes of through 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, is the only guaranteed type.
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.