When does the Kotlin compiler emit Java wildcards in bytecode for a declaration-site-variant type, and what does `@JvmSuppressWildcards` / `@JvmWildcard` do?
answer
- Variance = metadata only; JVM = wildcards
- out -> ? extends, in -> ? super in generated signatures
- @JvmSuppressWildcards strips, @JvmWildcard forces
- final classes -> no wildcard generated
- matters for Java interop / reflection / serialization
basics
~10 sKotlin auto-generates Java wildcards when an out/in type appears in a method signature, so Java callers get correct variance. @JvmSuppressWildcards removes them; @JvmWildcard forces one where Kotlin would omit it.
solid answer
~40 sDeclaration-site variance lives only in Kotlin metadata; the JVM only understands wildcards. So when a covariant (`out`) type parameter appears in a method *return* or parameter, the compiler emits `? extends Bound` (and `? super` for `in`) so Java consumers see correct PECS variance. Kotlin omits the wildcard in positions where it's redundant (e.g. final classes, or to keep signatures readable), and you can override the heuristic: `@JvmSuppressWildcards` strips wildcards (giving an invariant signature, useful when Java needs an exact type), and `@JvmWildcard` forces one at a specific type position. Both can be applied at declaration, type, or even file scope. A classic need: a `List<out Foo>` parameter generates `List<? extends Foo>`, which can be awkward for some Java APIs — suppressing yields plain `List<Foo>`.
code
kotlin · 8 lines// default: wildcard generated
fun a(xs: List<out Number>) {} // Java: List<? extends Number>
// suppressed: invariant signature
fun b(xs: List<@JvmSuppressWildcards Number>) {} // Java: List<Number>
// forced even where omitted
fun c(xs: List<@JvmWildcard Number>) {} // Java: List<? extends Number>go deeper
Aware that Kotlin variance becomes Java wildcards but fuzzy on the mechanics.
Can state out->? extends / in->? super generation and that final classes skip wildcards.
Explains the two override annotations precisely and a real interop scenario (reflection/serialization) requiring them.
Weighs binary-compatibility and API-design implications of emitting vs suppressing wildcards across a public Java-facing surface.
## Why translation is needed The JVM has no concept of declaration-site variance. `interface Source<out T>` carries its variance in Kotlin `@Metadata`, but a Java compiler only reads the erased + wildcard signatures. To preserve PECS for Java callers, the Kotlin compiler **injects wildcards** when a variant type parameter is *used* in a signature. ## Default wildcard generation rules - An `out T` parameter type generates `? extends Bound`. - An `in T` parameter type generates `? super Bound`. - Wildcards are generated mainly for **parameter** positions and other places where Java needs them to call the method covariantly/contravariantly. - Kotlin **omits** wildcards when they'd be redundant or harmful: e.g. when the argument type's class is `final` (no subtypes, so a wildcard adds nothing), or for return types in many cases. ```kotlin // Kotlin fun produce(list: List<out Number>) {} // Generated Java signature: // void produce(java.util.List<? extends Number> list) ``` ## `@JvmSuppressWildcards` Forces the compiler to emit the **invariant** type — no wildcard. ```kotlin fun produce(list: List<@JvmSuppressWildcards Number>) {} // Generated: void produce(List<Number> list) ``` Useful when a Java framework (reflection, serialization, some builders) needs the exact reified-looking type and a `? extends` confuses it. Can annotate a single type, a function, a class, or a whole file; it cascades to nested types. ## `@JvmWildcard` Forces a wildcard **where Kotlin would have omitted one**. ```kotlin fun produce(list: List<@JvmWildcard Number>) {} // Generated: void produce(List<? extends Number> list) ``` The two annotations are opposites and can be nested: an outer `@JvmSuppressWildcards` with an inner `@JvmWildcard` lets you control variance per type argument. ## Why you'd care - **Java interop ergonomics**: a generated `? extends T` can break Java code that expects an exact `T`. - **Reflection / annotation processors** (e.g. some DI or serialization libs) read the generic signature and misbehave on wildcards. - **Source vs binary compatibility**: changing whether a wildcard is emitted changes the public Java-visible signature, a potential binary-compat break. ## Key takeaways - Variance is Kotlin-only metadata; wildcards are the JVM representation. - The compiler translates automatically following PECS. - `@JvmSuppressWildcards` / `@JvmWildcard` are the manual overrides when the heuristic doesn't match what a Java consumer needs.
- Why might Kotlin NOT emit a wildcard for a `final` class type argument?A final class has no subtypes, so `? extends Final` is equivalent to `Final`; omitting the wildcard yields a cleaner, equally-safe signature.
- Can `@JvmSuppressWildcards` be applied to a whole file?Yes — as a file-level annotation (`@file:JvmSuppressWildcards`) it cascades to every declaration in the file.
- Why do serialization/DI libraries sometimes need wildcards suppressed?They reflect over the generic signature expecting an exact type token; a `? extends` wildcard produces a `WildcardType` they don't resolve, causing failures.
saying these in an interview costs you the question
- Claiming the JVM understands declaration-site variance directly
- Saying wildcards are never generated automatically
- Confusing the two annotations' directions (suppress vs force)
- Asserting wildcard generation never affects binary compatibility
- Thinking `@JvmSuppressWildcards` changes runtime behavior rather than the signature