What is wildcard capture in Java generics, and when does the compiler perform it?
answer
- Fresh type variable CAP#1 for an unknown wildcard
- JLS 5.1.10 capture conversion
- Happens when wildcard meets a type parameter
- 'capture of ?' in error messages
- You can't name the captured type yourself
basics
~20 sWildcard capture is when the compiler replaces a '?' with a fresh, made-up type variable so it can reason about it as a single concrete type. It happens when a method takes a wildcard-typed argument and binds it to a real type parameter.
solid answer
~50 sA wildcard like List<?> means 'a list of some specific but unknown type.' The compiler cannot name that type, so when it needs to treat it as one consistent type — for example to call a generic method whose signature ties parameter and return together — it performs *capture conversion*: it invents a fresh type variable, conventionally written CAP#1, that stands for whatever the wildcard actually is. This is why an error message may mention 'capture of ?'. Capture lets the compiler verify, say, that an element read from a list can be put back into the same list, even though you never named the element type. It happens automatically when a wildcard-typed expression is passed where a type parameter is expected; you cannot trigger or refer to a captured type yourself, which is why some operations on raw wildcards only work via a generic helper method.
go deeper
May not have heard of capture; recognizing the term and that it relates to wildcards is enough.
Knows wildcards mean an unknown type and can read a 'capture of ?' error, even if hazy on the mechanism.
Defines capture conversion as introducing a fresh type variable, explains when it triggers, and links it to the helper-method idiom.
Cites JLS semantics, reasons about why fresh-per-site preserves soundness, and discusses inference interactions and edge cases (nested wildcards, capture and overload resolution).
## The problem capture solves A wildcard `List<?>` (or `List<? extends Number>`) denotes "a list of *some single* unknown type T0." The crucial word is *single*: each individual `List<?>` value really has one fixed element type at runtime — you just can't name it in source code. The compiler, however, often needs to reason about that single type to prove safety. Consider swapping two elements of a `List<?>`. Reading from a `List<?>` gives `Object`, and writing requires the exact type, so a naive `list.set(i, list.get(j))` fails: `get` returns `Object`, but `set` won't accept an `Object`. Yet the operation is obviously safe — the element came from the very same list. ## What capture conversion is When a wildcard-typed expression is used where the compiler needs a concrete type parameter, it applies **capture conversion** (JLS §5.1.10): it introduces a **fresh type variable** — a brand-new, unnamed type the compiler invents — to stand for the wildcard's actual type. In error messages this appears as `capture#1 of ?` or `CAP#1`. For `? extends Number` the fresh variable has upper bound Number; for `? super Integer` it has lower bound Integer. Now within that scope the list behaves like `List<CAP#1>`: a value read out has type `CAP#1`, and `set` accepts a `CAP#1`. So the read element can be written back. ## When it happens Capture occurs **only when a wildcard type is matched against a type variable** — typically passing a `List<?>` argument to a method `<T> void m(List<T> l)`. It does **not** happen for plain field access or methods that don't involve a type variable. You cannot write the captured type yourself; you cannot do `CAP#1 x = ...`. This is why the standard trick is to delegate to a **private generic helper** whose type parameter captures the wildcard. ## Why you can't just "name" the type Two `List<?>` values may have *different* actual types, so a single name in scope can't be shared between them safely. Capture deliberately produces a fresh, distinct variable per capture site, so the compiler never accidentally unifies two unrelated unknown types. ## Takeaways - Wildcard = some single unknown type; capture = the compiler giving that unknown a temporary name. - Triggered by inference when a wildcard meets a type variable. - Lives only inside the compiler's reasoning; not nameable in source. - Enables self-referential operations (read-then-write-back) on wildcard collections via a generic method.
- Why does the compiler use a fresh variable per capture instead of reusing one?Two wildcard values may have different actual types; a shared name could unify unrelated unknowns and break soundness. A fresh variable keeps each unknown distinct.
- Does capture occur when you just call list.size() on a List<?>?No — size() does not involve a type variable, so no capture is needed. Capture is triggered only when a wildcard is matched against a type parameter.
saying these in an interview costs you the question
- Saying capture replaces ? with Object
- Thinking capture happens on every wildcard use
- Claiming you can declare a variable of the captured type
- Confusing capture with type erasure
- Believing two List<?> get the same captured variable