Why can you read from a List<? extends Number> but not add elements to it (other than null), and what is the inverse rule for List<? super Integer>?
answer
- PECS: Producer Extends, Consumer Super
- extends = read-safe, write-blocked (except null)
- super = write-safe (T & subtypes), read gives Object
- Invariance is the reason wildcards exist
- Unknown exact type -> can't prove add is legal
basics
~20 sWith '? extends Number' the exact type is unknown, so the compiler can't be sure a value you add is allowed, but every element is at least a Number so reading is safe. With '? super Integer' the reverse holds: you can add Integers, but reads only give you Object.
solid answer
~40 sA wildcard stands for some single but unknown type. With List<? extends Number> the list could really be List<Integer> or List<Double>, so the compiler cannot let you add anything (a Double would be illegal if it is actually List<Integer>); the only value provably assignable to every possibility is null. Reading is safe because whatever the element type is, it is guaranteed to be a Number. List<? super Integer> is the mirror: it could be List<Integer>, List<Number>, or List<Object>, so adding an Integer (or any subtype) is always safe, but reading gives back only Object because that is the one type known to be a supertype of all possibilities. This is the basis of PECS: Producer Extends, Consumer Super.
code
java · 9 linesList<? extends Number> producer = new ArrayList<Integer>();
Number n = producer.get(0); // OK: element is at least Number
// producer.add(4); // compile error: unknown exact type
producer.add(null); // OK: null fits any element type
List<? super Integer> consumer = new ArrayList<Number>();
consumer.add(42); // OK: Integer fits Integer/Number/Object
Object o = consumer.get(0); // OK, but only as Object
// Integer i = consumer.get(0); // compile errorgo deeper
Knows you can read from '? extends' and write to '? super', and can recite PECS even if shaky on why.
Explains both restrictions in terms of the unknown exact type and gives the null exception; applies PECS to a copy method.
Connects the rules to invariance and type soundness, writes correct bounded signatures, and reasons about the get-returns-Object case for super.
Frames it as variance in the type system, contrasts with declaration-site variance in other languages (Kotlin/Scala), and weighs API ergonomics of exposing wildcards.
## Background **Generics** parameterize a type: `List<String>` is a list of `String`. The thing in angle brackets is the *type argument*. Generics are **invariant**: even though `Integer` is a subtype of `Number`, `List<Integer>` is NOT a subtype of `List<Number>`. If it were, you could store a `Double` through a `List<Number>` reference that actually points at a `List<Integer>`, corrupting it. So the compiler forbids that assignment. A **wildcard** `?` relaxes invariance safely. `List<? extends Number>` means "a List of *some* unknown type that is Number or a subtype of Number." `List<? super Integer>` means "a List of some unknown type that is Integer or a *supertype* of Integer." ## Why you can't add to `List<? extends Number>` The reference could point at a `List<Integer>`, a `List<Double>`, or a `List<Number>` — the compiler does not know which. To call `list.add(x)`, the compiler must prove `x` is assignable to the real element type. Since that type is unknown (only known to be "some subtype of Number"), no concrete value qualifies. If you tried `list.add(new Double(1))` and the list were really `List<Integer>`, you would corrupt it. The *only* value assignable to every possible subtype is `null`, so `list.add(null)` compiles. ## Why you CAN read it as Number Whatever the real element type is, it is guaranteed to be Number-or-below. So `Number n = list.get(0);` is always type-safe — the element is at least a Number. ## The inverse: `List<? super Integer>` Now the reference could point at `List<Integer>`, `List<Number>`, or `List<Object>` — some unknown supertype of Integer. - **Adding** is safe: an `Integer` is assignable to Integer, Number, and Object alike. So `list.add(42)` compiles. - **Reading** loses information: the element could be Integer, Number, or Object, so the only type the compiler can guarantee for a read is `Object`. `Object o = list.get(0);` works; `Integer i = list.get(0);` does not. ## PECS Joshua Bloch's mnemonic: **Producer Extends, Consumer Super**. If a parameterized type *produces* values you consume (you read from it), use `extends`. If it *consumes* values you supply (you write to it), use `super`. A method that copies typically takes a `? extends T` source (producer) and a `? super T` destination (consumer). ## Summary table | Type | add(x) | get() returns | |---|---|---| | `List<? extends Number>` | only `null` | `Number` | | `List<? super Integer>` | `Integer` and subtypes | `Object` | | `List<?>` (unbounded) | only `null` | `Object` |
- What is the one value you can always add to a List<? extends Number>?null — it is assignable to every possible element type, so it is the only add that the compiler can prove safe.
- Write the signature of Collections.copy using PECS.static <T> void copy(List<? super T> dest, List<? extends T> src) — src produces (extends), dest consumes (super).
saying these in an interview costs you the question
- Saying you can add any Number to List<? extends Number>
- Claiming List<Integer> is a subtype of List<Number>
- Thinking '? super Integer' lets you read back an Integer
- Confusing extends/super directions of PECS
- Forgetting that null is the one value you can always add