Detailed explanation
In Java generics, '? extends T' and '? super T' describe an unknown type argument with a bound. '? extends T' means the unknown type is T or a subtype of T; '? super T' means it is T or a supertype of T. They make APIs flexible while preserving compile-time type safety. This matters because generics are invariant: List<Integer> is not a subtype of List<Number>, even though Integer extends Number.
A List<? extends Number> can refer to a List<Integer>, List<Double>, or List<Number>. Reading an element as Number is safe because every possible element type extends Number. However, adding an Integer, Double, or even a Number is rejected: the compiler cannot know the list's actual element type. For example, adding an Integer would be unsafe if the underlying list were a List<Double>. The wildcard permits adding null at compile time, though the implementation may reject it. This does not make the list immutable; operations such as clear() or removal may still be supported.
A List<? super Integer> can refer to a List<Integer>, List<Number>, or List<Object>. Adding an Integer is type-safe for all these possibilities. Reading gives only Object as the guaranteed static type, because the list might also contain non-integer objects. This leads to PECS: Producer Extends, Consumer Super. Choose the bound according to how the method uses the collection: a producer supplies values to the method, while a consumer receives values from it. Under the hood, the compiler treats each wildcard as a captured unknown type and checks operations against its bounds; type erasure does not bypass these checks.
The supplied copy example is unsafe: List<? extends Number> may contain Double values, so casting each Number to Integer can throw ClassCastException. A safe reusable signature is static <T> void copy(List<? extends T> src, List<? super T> dest), with the body for (T value : src) dest.add(value);. The shared T connects source and destination types without a cast. Interviewers look for this reasoning, not just memorisation of PECS.
Key points to remember
- '? extends T' is an upper-bounded wildcard: read values as T, but do not add non-null values.
- '? super T' is a lower-bounded wildcard: add T values, but read only as Object without further checks.
- PECS describes the collection's role relative to the method: Producer Extends, Consumer Super.
- Wildcards provide use-site flexibility; they do not make List<Integer> a subtype of List<Number>.
- Use a shared type parameter to connect source and destination types in a safe generic copy method.
Common mistakes candidates make
- Casting every Number to Integer in the supplied example; a source containing Double values makes that cast fail.
- Calling '? extends T' immutable or completely read-only; it restricts insertion, not every mutation.
- Assuming '? super Integer' allows adding any Number or guarantees that reads return Integer.
Likely follow-up questions
How does List<?> differ from List<Object>?
List<?> is a list with an unknown element type, whereas List<Object> has Object as its element type. You can add arbitrary object values to List<Object>, but only null is permitted by the type system for List<?>.
When should I use List<T> rather than a bounded wildcard?
Use List<T> when the method needs to both read and write values of the same element type. Use a named type parameter when you need to express relationships between arguments or between an argument and the return type.
Which calls are safe with the corrected copy method?
Copying from List<Integer> into List<Number> or List<Object> is type-safe. Copying from List<Number> into List<Integer> is rejected because a Number source could supply a Double or another non-Integer value.
Interview tip: Explain PECS using Integer, Number, and Object, then show the cast-free generic copy signature to demonstrate type-safety reasoning.
