PECS — Producer-Extends, Consumer-Super: The One Rule That Solves Most Wildcard Decisions
Fast recall
Quick revision
Core answer
? extends T. If the parameter consumes values your method writes, declare it ? super T. If it does both (reads and writes T), don't use wildcards — use a named type parameter . Canonical example: Collections.copy(List super T> dest, List extends T> src) — source produces T, destination consumes T. The same rule applies to streams (Stream.forEach(Consumer super T>)), comparators (Comparator super T>), and any method that reads from or writes to a collection.Whether the candidate has internalized PECS as a decision rule, can apply it to real JDK signatures, and knows when NOT to use wildcards
Applying PECS mechanically without understanding why — failing to notice when the same parameter both produces and consumes. Using raw types instead of either wildcard or named parameter
Walk through why `Stream.forEach(Consumer<? super T>)` uses `? super` (the consumer accepts T, so a consumer of a supertype also works). Mention the 'get and put' principle as a more operational version of PECS
Interview-ready guide
Interview answer
PECS is the one rule that collapses most wildcard decisions into a simple question: "does my method read from this parameter, write to it, or both?"
The rule
- Producer Extends — if the parameter is a source you read from, use
? extends T - Consumer Super — if the parameter is a sink you write to, use
? super T - If the parameter does both, don't use wildcards — use a named type parameter
Canonical example — `Collections.copy`
copy(List super T> dest, List extends T> src)- Source is a producer of T — you only read from it — so
? extends T - Destination is a consumer of T — you only write to it — so
? super T - This lets you call
copy(List— source is a subtype, destination is a supertype
Why it works
? extends T: any subtype fits; safe to read as T; can't add because the compiler doesn't know the exact subtype? super T: any supertype fits; safe to add T and subtypes; reads come back as Object because we only know it's at least T- Named
: when you need to both read and write T, wildcards can't express that it's the same T
PECS is everywhere in the JDK
Stream.forEach(Consumer super T>)— consumer accepts T, so supertype consumers workCollections.sort(List— comparator only consumes T, Comparator super T>) Stream.map(Function super T, ? extends R>)— function consumes T (super T), produces R (extends R)
When NOT to use wildcards
- Return types — they force every caller to use wildcard types
- Two parameters that must share the same type — wildcards lose the relationship
Recommendation: Memorize PECS and the 'get and put' mental model — they'll carry you through 95% of API design decisions. Use ? extends T for inputs you only read, ? super T for inputs you only write, and a named when you need both. Don't wildcard return types, and never fall back to raw types.
PECS — Producer-Extends, Consumer-Super: The One Rule That Solves Most Wildcard Decisions
Direct Answer
PECS — Producer-Extends, Consumer-Super — is Joshua Bloch's rule for picking the right wildcard. If the parameter produces values your method reads, declare it ? extends T. If the parameter consumes values your method writes, declare it ? super T. If it does both (reads and writes T), don't use wildcards — use a named type parameter <T>. Canonical example: Collections.copy(List<? super T> dest, List<? extends T> src) — source produces T, destination consumes T. The same rule applies to streams (Stream.forEach(Consumer<? super T>)), comparators (Comparator<? super T>), and any method that reads from or writes to a collection.
Common Mistakes & Pitfalls
Applying PECS mechanically without understanding why — failing to notice when the same parameter both produces and consumes. Using raw types instead of either wildcard or named parameter
Check your understanding
Answer these without looking back at the article.
- Why does `Stream.forEach` take a `Consumer<? super T>` instead of `Consumer<T>`?
- Can you apply PECS to return types? Why is it discouraged?
- Why does `Stream.map` use `Function<? super T, ? extends R>` with both wildcards?
- What does the 'get and put' principle say about methods that only get, only put, or do both?
- How does PECS interact with generic method signatures like `<E extends Comparable<? super E>>`?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.