Why can you only read elements as `Object` from a `List<? super T>`, and what is the underlying type-safety reasoning?
answer
- element type = unknown supertype of T
- only Object is common to all supertypes
- read precise type → CCE risk → forbidden
- contravariant = write-friendly, read-Object
- wrong wildcard if you need typed reads
basics
~20 sBecause the list's real element type could be T or any of its parents, the compiler doesn't know the exact type. The only type guaranteed to fit every possibility is Object, so reads come back as Object.
solid answer
~40 s`List<? super T>` means the actual element type is some unknown supertype of T — it could be T, a parent, a grandparent, up to Object. When you read an element, the compiler must hand you a type that is correct *regardless of* which supertype it really is. The narrowest type that every Java reference is guaranteed to be assignable to is `Object`. So a `get()` on a `<? super T>` reference is typed as Object; assigning it to anything more specific (even T) is a compile error, because the runtime type might be a more general supertype. This is the deliberate cost of the contravariant, write-oriented wildcard: it trades read precision for write flexibility, which is exactly right for a consumer/sink role.
go deeper
Knows that reads from <? super T> give Object and not T.
Explains the soundness reasoning: the element type is an unknown supertype, so only Object is guaranteed.
Frames it as contravariance, contrasts with <? extends T> reads, and chooses the wildcard by read/write need.
Connects the rule to type-system soundness/erasure, and can explain why the alternative would reintroduce unchecked-cast unsoundness.
## Setup Consider `List<? super Integer>`. The `? super Integer` part is a **lower-bounded wildcard**: the real element type is **Integer or any supertype of Integer**. Concretely the list could actually be: - `List<Integer>` - `List<Number>` - `List<Comparable<Integer>>` (an interface Integer implements... actually Integer's supertypes include Number, Comparable, Serializable, Object) - `List<Object>` The compiler does **not** know which one — only that it is *somewhere at or above Integer* in the hierarchy. ## The read question When you call `get(i)`, the compiler must assign a static type to the result that is **sound for every possibility** above. Ask: what type are the elements *guaranteed* to be? - If the list is `List<Integer>`, elements are Integers. - If it is `List<Number>`, elements are Numbers (might be a Double!). - If it is `List<Object>`, elements are arbitrary Objects (might be a String!). The **only** type that holds across *all* of these is `Object`. So: ```java List<? super Integer> list = ...; Object o = list.get(0); // OK — always sound Integer i = list.get(0); // COMPILE ERROR — the element might be a Double or a String ``` If the compiler let you read into `Integer`, and the underlying list were a `List<Object>` containing a `String`, you would get a `ClassCastException` at runtime with no cast in your source — violating generics' core promise of compile-time type safety. ## Contrast with the write side Writes are the mirror image. Adding an `Integer` (or subtype) is always safe, because whatever the real element type is, it is a supertype of Integer, and an Integer **is-a** that supertype: ```java list.add(99); // OK Object result = list.get(0); // read back only as Object ``` So a `<? super T>` reference is **good at writing, weak at reading** — the textbook **consumer/sink**. ## The deeper principle: contravariance `<? super T>` makes the type **contravariant** in T: it flips the usual subtype direction for *inputs*. Contravariant positions are exactly the positions you can safely *put values into* but not *pull typed values out of*. The `get → Object` limitation is not an arbitrary rule; it is forced by soundness. ## Practical takeaway If your code needs to **read typed T values**, you have chosen the wrong wildcard — switch to `<? extends T>` (covariant, producer). Use `<? super T>` only where the collection is a destination you write into. This is the read half of the PECS rule.
- If you genuinely need to read elements as T, which wildcard should you use instead?`<? extends T>` — the upper-bounded, covariant wildcard. Reads return T; the trade-off is you can no longer add elements (except null).
- What runtime error would occur if the compiler wrongly allowed `Integer i = list.get(0)` on a `List<? super Integer>` backed by a `List<Object>` holding a String?A ClassCastException at the implicit cast, defeating the compile-time safety guarantee of generics.
saying these in an interview costs you the question
- Expecting `get()` to return T from a `<? super T>` list.
- Thinking the restriction is arbitrary rather than forced by soundness.
- Believing you can cast the Object result safely without runtime risk.
- Confusing the read behavior with `<? extends T>`, which returns T.