Apply PECS to design a generic `addAll(Collection<T> src, Collection<T> dst)` and a `max(Collection<T>)` method. What wildcards do you use?
answer
- tag each param producer/consumer
- src extends, dst super
- max(Collection<? extends T>)
- Comparable<? super T> (compareTo consumes)
- no wildcards in return types
basics
~20 sFor copying, the source produces so use ? extends T, and the destination consumes so use ? super T. For finding a max, the collection only produces elements to compare, so use ? extends T. Wildcards let the methods accept more caller types.
solid answer
~40 sWalk each parameter by its role. For a copy/addAll-style method, the source is read from — it produces T — so `Collection<? extends T> src`; the destination is written to — it consumes T — so `Collection<? super T> dst`. That lets you pour a `Collection<Integer>` into a `Collection<Number>`. For `max(Collection<? extends T> c)`, the collection only yields elements to compare, so it is a producer — `? extends T`. The comparison itself adds another wildcard: the elements must be `Comparable` against themselves or a supertype, so you write `<T extends Comparable<? super T>>`. The general method: tag each parameter producer or consumer, give producers `extends`, consumers `super`, leave a parameter that does both as plain `T`, and avoid wildcards in the return type so callers are not forced to handle them.
code
java · 10 linesstatic <T> void addAll(Collection<? extends T> src, Collection<? super T> dst) {
for (T t : src) dst.add(t);
}
static <T extends Comparable<? super T>> T max(Collection<? extends T> c) {
Iterator<? extends T> it = c.iterator();
T best = it.next();
while (it.hasNext()) { T x = it.next(); if (x.compareTo(best) > 0) best = x; }
return best;
}go deeper
Can state which parameter is the producer and which is the consumer in a copy method.
Writes a correct addAll signature and explains why the wildcards widen the accepted argument types.
Derives the full max signature including Comparable<? super T>, and follows the no-wildcards-in-returns rule.
Establishes API-design conventions (where wildcards belong, return-type policy) and reviews library signatures for correct variance across many methods.
## Recap of the rule PECS — **Producer Extends, Consumer Super** — tells you which bounded wildcard to put on each *parameter* of a generic method. For each parameter ask: does the method **read** T values out of it (producer → `? extends T`) or **write** T values into it (consumer → `? super T`)? If it does both with the same type, use plain `T`. Wildcards exist because Java generics are **invariant** (`List<Integer>` is not a `List<Number>`), which otherwise makes generic methods reject obviously-safe arguments. ## Designing addAll(src, dst) Think about data flow. Elements are read from `src` and written into `dst`. - `src` produces T → `Collection<? extends T> src` - `dst` consumes T → `Collection<? super T> dst` ```java static <T> void addAll(Collection<? extends T> src, Collection<? super T> dst) { for (T t : src) { // read (produce) from src dst.add(t); // write (consume) into dst } } ``` Now `addAll(listOfIntegers, listOfNumbers)` compiles, because `Integer extends Integer` (or `Number`) satisfies the producer bound and `Number super Integer` satisfies the consumer bound. With plain `Collection<T>` for both, callers would be forced to make the element types identical. ## Designing max(Collection) `max` only **reads** elements to compare them and returns one — the collection is a pure producer: ```java static <T extends Comparable<? super T>> T max(Collection<? extends T> c) { Iterator<? extends T> it = c.iterator(); T best = it.next(); while (it.hasNext()) { T next = it.next(); if (next.compareTo(best) > 0) best = next; } return best; } ``` Two wildcards appear: 1. `Collection<? extends T>` — the collection produces T's, so `extends`. 2. `Comparable<? super T>` — this is PECS applied to `Comparable`. `compareTo` **consumes** a T (you pass a T into it), so the comparator side is a consumer → `super`. This lets a `T` be compared using a `compareTo` it inherited from a *supertype* (e.g. an `Integer` compared via `Comparable<Number>` logic if that existed). It maximizes which types qualify. This exact signature is essentially `Collections.max` in the JDK — a textbook double application of PECS. ## The two guiding heuristics 1. **Tag every parameter** producer/consumer/both, then apply `extends`/`super`/plain `T`. 2. **Do not use wildcards in return types.** A wildcard return leaks into the caller's code and forces them to deal with wildcards. Keep wildcards on inputs; return a concrete `T`. ## When a parameter is both If the method both reads and writes the same type through one parameter (e.g. an in/out buffer), a wildcard cannot serve both safely — use exact `List<T>`. ## Terms defined - **Bounded wildcard:** `? extends T` (upper bound) or `? super T` (lower bound). - **Invariance:** parameterized types are not subtype-related just because their arguments are. - **Comparable<? super T>:** lets T be comparable using comparison logic defined on T or any of its supertypes — PECS applied to the comparator-as-consumer.
- Why is the comparison bound `Comparable<? super T>` rather than `Comparable<T>`?Because compareTo consumes a T (you feed it the element), so by PECS the comparator is a consumer → super. It lets a type be compared using compareTo inherited from a supertype, widening which types qualify.
- Should max's return type use a wildcard?No. It returns a concrete `T`. Wildcards belong on inputs; a wildcard return would force every caller to handle wildcards.
saying these in an interview costs you the question
- Putting wildcards on the return type and burdening callers
- Using `? super T` on the source you read from
- Forgetting the `Comparable<? super T>` bound and over-restricting valid types
- Using a wildcard when a parameter both reads and writes the same T