PECS Rule in Java Generics

Naveen AnamaneniUpdated · 4 min read

Choosing between ? extends and ? super confuses almost everyone at first. Joshua Bloch, author of Effective Java, summed up the choice in four letters: PECS, "Producer Extends, Consumer Super". Ask one question about each parameter: does my method take values out of it (it's a producer) or put values into it (it's a consumer)? The answer picks the wildcard for you.

Producer Extends, Consumer Super

Values come out of a producer (extends) and go into a consumer (super)
Pecs.java▶ Try it
import java.util.ArrayList;
import java.util.List;
 
public class Pecs {
    static <T> void copyInto(List<? extends T> source, List<? super T> target) {
        for (T item : source) {
            target.add(item);
        }
    }
 
    public static void main(String[] args) {
        List<Integer> marks = List.of(91, 88, 95);
        List<Double> fees = List.of(1200.5, 800.0);
 
        List<Number> all = new ArrayList<>();
        copyInto(marks, all);
        copyInto(fees, all);
        System.out.println(all);
 
        List<Object> anything = new ArrayList<>(List.of("Srihitha"));
        copyInto(marks, anything);
        System.out.println(anything);
    }
}
Output
[91, 88, 95, 1200.5, 800.0]
[Srihitha, 91, 88, 95]

How it works

  1. source is a producer: the method only reads from it. So it's List<? extends T>, which accepts a list of T or of any subtype.
  2. target is a consumer: the method only adds to it. So it's List<? super T>, which accepts a list of T or of any supertype.
  3. With T = Number, integers and doubles both copy into a List<Number>. With T = Integer, marks copy into a List<Object>.
  4. With plain List<T> for both, none of these calls would compile, because each list type would have to match exactly.

The Decision Table

The method...The parameter is a...Declare it as
Only reads values out of itProducerList<? extends T>
Only writes values into itConsumerList<? super T>
Reads and writesBothList<T>
Neither (only size, clear)NeitherList<?>

PECS in the JDK

The JDK applies the rule everywhere, which is why its methods accept so many argument types:

MethodProducerConsumer
Collections.copy(List<? super T> dest, List<? extends T> src)srcdest
Collection.addAll(Collection<? extends E> c)cthe collection itself
Collections.max(Collection<? extends T> coll)coll
List.sort(Comparator<? super E> c)c consumes elements to compare them

The last row surprises people: a Comparator<Object> can sort a List<String>, because a comparator that can compare any objects can certainly compare strings. The comparator consumes elements, so it's ? super E.

Common Mistakes

Using ? extends for a list you add to. The compiler rejects every add.

Using ? super for a list you read typed values from. You only get Object back.

Using wildcards on a parameter you both read and write. Use a plain type parameter T.

Interview Corner

What is the PECS rule?

PECS stands for "producer extends, consumer super". If a generic parameter only produces values that your method reads, declare it with ? extends T; if it only consumes values that your method writes, declare it with ? super T. A parameter used for both should use the exact type T. Collections.copy(List<? super T> dest, List<? extends T> src) follows the rule.

Practice

  1. Easy: For each parameter, choose ? extends, ? super or plain T: a list you sum; a list you fill with defaults; a list you sort in place.
  2. Harder: Write static <T> void moveAll(List<? extends T> from, List<? super T> to, Predicate<? super T> keep) that copies only the items the predicate accepts.

Quick check: PECS

4 questions
1A method only reads values from a list. Which wildcard?
2A method only adds values to a list. Which wildcard?
3In Collections.copy(dest, src), which is the producer?
4Why is List.sort declared with Comparator<? super E>?

Was this tutorial helpful?

Lesson 246 of 482