Apply PECS: in a method that copies elements from one list to another, which parameter gets `<? extends T>` and why?
answer
- PECS = Producer Extends, Consumer Super
- source = producer = extends
- dest = consumer = super
- both read+write → use plain List<T>
- Collections.copy(dest super, src extends)
basics
~10 sThe source list (the one you read from) gets <? extends T> because it produces elements. The destination list (the one you write to) gets <? super T> because it consumes them.
solid answer
~40 sPECS means Producer Extends, Consumer Super. In a `copy(dest, src)` method, `src` is the producer — you only read elements out of it — so it should be `List<? extends T>`, which lets callers pass a `List<T>` or any `List` of a subtype. `dest` is the consumer — you only write elements into it — so it should be `List<? super T>`, accepting a `List<T>` or any `List` of a supertype. This is exactly how `Collections.copy(List<? super T> dest, List<? extends T> src)` is declared. Using `extends` on the source widens the set of acceptable source lists while keeping reads type-safe (each element is guaranteed a `T`). If you used a bare `List<T>` for the source instead, you'd needlessly reject `List<SubtypeOfT>` callers. The mnemonic captures the asymmetry: covariant for producers, contravariant for consumers.
code
java · 13 lines// Producer Extends, Consumer Super (the real Collections.copy signature)
public static <T> void copy(List<? super T> dest, // consumer: we write into it
List<? extends T> src) { // producer: we read from it
for (int i = 0; i < src.size(); i++) {
T value = src.get(i); // read guaranteed to be a T (upper bound)
dest.set(i, value); // write a T into a list of some supertype of T
}
}
// Usage that only compiles thanks to the wildcards:
List<Object> dest = new ArrayList<>(List.of(0, 0, 0));
List<Integer> src = List.of(1, 2, 3);
copy(dest, src); // T = Integer; src is List<? extends Integer>, dest is List<? super Integer>go deeper
Can recite PECS and identify that the list you read from gets extends.
Correctly assigns extends to the source and super to the destination in a copy method and explains why each widens callers.
Reproduces the Collections.copy signature from first principles, knows the 'both producer and consumer → use plain T' rule, and cites other JDK uses.
Designs public APIs with wildcards that maximize caller flexibility, weighs wildcard vs bounded type variable for usability, and reasons about variance interactions across an API surface.
## The problem PECS solves When you write a generic method that moves data, you want the **widest** set of callers to be able to use it without sacrificing type safety. Wildcards are the tool; PECS tells you which wildcard to put where. **PECS = Producer Extends, Consumer Super** (coined by Joshua Bloch in *Effective Java*). - A **producer** is a parameter you only **read from** (it produces values you consume). Give it `<? extends T>`. - A **consumer** is a parameter you only **write to** (it consumes values you provide). Give it `<? super T>`. ## Worked example: a copy method ```java public static <T> void copy(List<? super T> dest, List<? extends T> src) { for (int i = 0; i < src.size(); i++) { dest.set(i, src.get(i)); } } ``` Reasoning element by element: - `src.get(i)` **produces** a value. We only read from `src`, so `src` is the producer → `List<? extends T>`. Each read is guaranteed to be a `T` (the upper bound), which is exactly the type we hand to `dest`. - `dest.set(i, value)` **consumes** the value. We only write to `dest`, so `dest` is the consumer → `List<? super T>`. A `List` of any supertype of `T` can safely hold a `T`. This is the real signature of `java.util.Collections.copy`. ## Why `extends` on the source widens callers If `src` were `List<T>`, then `copy(dst, new ArrayList<Integer>())` with `T = Number` would **fail** — `List<Integer>` is not a `List<Number>` (invariance). With `List<? extends Number>`, it compiles, because `List<Integer>` IS a `List<? extends Number>` (covariance). So `extends` on the producer maximizes the source lists you can pass. ## Why `super` on the destination widens callers Symmetrically, `dest` as `List<? super Number>` lets you copy `Number`s into a `List<Number>` **or** a `List<Object>` — any list that can hold a `Number`. That's contravariance: it varies opposite to the element subtyping. ## The "both" case: don't use a wildcard If a parameter is **both** produced from and consumed into within the same method, neither wildcard works — you can't read-as-`T` AND write-as-`T` through a single wildcard. Use a plain type parameter `List<T>` there. The rule of thumb: "if a type parameter appears only once in the method and you only read or only write, a wildcard is right; if you do both, use the type variable." ## Other library examples - `Collections.max(Collection<? extends T> coll)` — reads (produces) elements to compare. - `Stream.forEach(Consumer<? super T> action)` — the consumer accepts a `T`, so `super`. - `Comparator` / `Comparable` signatures lean on `super` heavily because they consume the compared object. ## Common mistakes - Putting `extends` on the destination — then you can't write to it (it's read-only). - Putting `super` on the source — then reads only give you `Object`, losing the `T` guarantee. - Using wildcards on a parameter that is both produced and consumed. PECS turns the abstract covariance/contravariance rules into a one-line decision procedure you can apply at the keyboard.
- When should a parameter use a plain type variable `T` instead of a wildcard?When the method both reads from and writes to that parameter (it is both producer and consumer), a single wildcard can't serve both, so you use `List<T>`.
- What is the wildcard in `Comparator<? super T>` (e.g. in `Collections.sort`) doing?It's the consumer side: a comparator consumes `T` values to compare, so allowing any comparator of a supertype of `T` widens the acceptable comparators.
saying these in an interview costs you the question
- Putting `extends` on the destination (makes it un-writable).
- Putting `super` on the source (reads degrade to Object).
- Using a wildcard on a parameter that is both read from and written to.
- Thinking PECS is about performance rather than type safety and caller flexibility.