skip to content

What does the PECS mnemonic stand for, and how do you decide between `? extends T` and `? super T`?

level: middleimportance: must knowfreq 72%

answer

  1. Producer Extends, Consumer Super
  2. extends = read-only (out), super = write-only (in)
  3. Collections.copy(dest super, src extends)
  4. both produce and consume -> plain T
  5. invariance is why wildcards exist

basics

~10 s

PECS means Producer Extends, Consumer Super. If a structure gives you (produces) values, use ? extends T. If it receives the values you put in (consumes them), use ? super T.

solid answer

~40 s

PECS stands for "Producer Extends, Consumer Super." It is a rule for picking the right bounded wildcard. If a generic parameter is a source you only read T values out of, declare it `? extends T` (a producer). If it is a sink you only write T values into, declare it `? super T` (a consumer). The canonical example is `Collections.copy(List<? super T> dest, List<? extends T> src)`: the source produces, so `extends`; the destination consumes, so `super`. The payoff is flexibility: `? extends Number` accepts `List<Integer>` and `List<Double>`; `? super Integer` accepts `List<Number>` and `List<Object>`. If a parameter both produces and consumes T, use an exact `List<T>` with no wildcard.

code

java · 10 lines
java
// Producer Extends, Consumer Super
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)); // read src (produces), write dest (consumes)
    }
}

List<Number> dest = new ArrayList<>(List.of(0, 0, 0));
List<Integer> src  = List.of(1, 2, 3);
copy(dest, src); // works: Number super Integer, Integer extends Integer

go deeper

for a junior

Knows the words "Producer Extends, Consumer Super" and that extends is for reading and super is for writing.

for a middle

Can apply PECS to a method signature, explains why a producer is read-only and a consumer is write-only, and recognizes when no wildcard is needed.

for a senior

Connects PECS to generic invariance and type-safety proofs, designs flexible API signatures, and knows to avoid wildcards in return types.

for a principal

Sets team conventions on wildcard usage at API boundaries, weighs flexibility vs. caller burden, and reasons about variance across the codebase and library design.

## The problem PECS solves Java generics are **invariant**: `List<Integer>` is *not* a subtype of `List<Number>`, even though `Integer` is a subtype of `Number`. The reason is safety. If `List<Integer>` were assignable to `List<Number>`, you could call `add(3.14)` (a `Double`) on it and corrupt a list meant to hold only `Integer`s. So Java forbids it. That invariance makes generic methods rigid: a method taking `List<Number>` rejects a `List<Integer>` argument. **Bounded wildcards** restore flexibility while keeping safety. ## The two bounded wildcards - **`? extends T`** — "some unknown type that is T or a subtype of T." `List<? extends Number>` could really be a `List<Integer>` or a `List<Double>`. Because the compiler does not know *which* subtype, it lets you **read** elements (they are guaranteed to be at least a `Number`) but **forbids adding** anything except `null` (you cannot prove a `Double` belongs in what might be a `List<Integer>`). This is a **producer**: it hands T values out. - **`? super T`** — "some unknown type that is T or a supertype of T." `List<? super Integer>` could be a `List<Integer>`, `List<Number>`, or `List<Object>`. You can safely **add** an `Integer` (it fits any of those), but **reading** gives back only `Object`, because you do not know how far up the hierarchy the real type is. This is a **consumer**: it takes T values in. ## The mnemonic **P**roducer **E**xtends, **C**onsumer **S**uper, coined by Joshua Bloch in *Effective Java*. Ask: *from the method's point of view, does this parameter produce T's (give them to me) or consume T's (receive them from me)?* - Produces T → `? extends T` - Consumes T → `? super T` - Both (read and write the same T) → plain `T`, no wildcard. - Neither / does not involve T at all → no wildcard needed. ## Worked example: Collections.copy ```java public static <T> void copy(List<? super T> dest, List<? extends T> src) ``` The `src` list is read from — it **produces** T's — so `? extends T`. The `dest` list is written to — it **consumes** T's — so `? super T`. With this signature you can copy a `List<Integer>` into a `List<Number>`, which would be impossible with plain `List<T>` for both. ## Why the asymmetry of read/write The restriction falls out of what the compiler can *prove*. With `? extends T` the lower edge of the real type is unknown, so writing is unsafe but the upper bound T is known, so reading is safe. With `? super T` the upper edge is unknown (reads degrade to `Object`) but T is a guaranteed lower bound, so writing a T is safe. Reading from a producer and writing to a consumer are exactly the safe operations — hence PECS. ## When NOT to use a wildcard If a parameter is both produced from and consumed into with the same type variable, you need an exact type `List<T>`. Also, return types should generally not use wildcards — they force the caller to deal with wildcards too. Wildcards are a tool for input flexibility.

  • Can you add elements to a `List<? extends Number>`?
    No — except the literal `null`. The compiler does not know the actual element type (could be `List<Integer>`), so it cannot prove any concrete value is safe to add.
  • What type comes back when you read from a `List<? super Integer>`?
    `Object`. The actual list could be `List<Object>`, so the only guaranteed upper bound for a read is `Object`.

saying these in an interview costs you the question

  • Saying you can add arbitrary elements to a `? extends T` list (you can only add null)
  • Confusing the direction: using extends for the destination/consumer
  • Claiming `List<Integer>` is a subtype of `List<Number>` (generics are invariant)
  • Thinking reading from a `? super T` list gives back T (it gives Object)

context