You maintain a Kotlin library consumed by Java. Walk through the tradeoffs of @JvmOverloads versus manual overloads, and explain the binary-compatibility risks of adding or reordering default parameters on a published API.
answer
- @JvmOverloads: right-to-left trailing overloads only
- No arbitrary subsets; middle omission has no Java overload
- Adding/reordering defaults changes $default descriptor + mask bits
- Old-compiled callers => NoSuchMethodError
- Treat default-param changes as ABI-breaking
basics
~10 s@JvmOverloads auto-generates Java-friendly overloads with no boilerplate but you control them less. Adding or reordering default parameters changes the synthetic $default signature and call-site bitmasks, so already-compiled callers can break.
solid answer
~40 s@JvmOverloads generates one telescoping overload per trailing default (right-to-left), removing boilerplate and keeping Kotlin and Java in sync. The cost: you can't customize individual overloads, and it generates only contiguous-from-the-right combinations, not arbitrary subsets. Manual overloads give full control but must be hand-maintained and risk drift. The deeper risk is binary compatibility: adding a default parameter changes the real method/constructor signature and the synthetic $default signature and shifts the bitmask layout, so callers compiled against the old version may throw NoSuchMethodError at runtime even though source still compiles. Reordering parameters changes mask bit assignment and overload shapes similarly. For a published API, recompilation of all consumers is required; treat default-parameter changes as ABI-breaking. Adding @JvmOverloads later also adds new methods (source-compatible) but is itself an ABI change for the method table.
code
kotlin · 4 lines@JvmOverloads
fun render(text: String, bold: Boolean = false, italic: Boolean = false) {}
// Generates: render(String), render(String, Boolean), render(String, Boolean, Boolean)
// Inserting a NEW default param between them is binary-breaking for precompiled callers.go deeper
Knows @JvmOverloads exists and removes overload boilerplate.
Explains right-to-left overload generation and that manual overloads are the alternative.
Identifies that adding/reordering defaults can break compiled callers and links it to the $default/mask mechanism.
Frames a versioning/API-stability policy: ABI vs source compatibility, NoSuchMethodError risk, and strategies (append-only, builders) for cross-language libraries.
## Two ways to give Java the convenience ### @JvmOverloads Generates, for `fun f(a, b = .., c = ..)`, the overloads `f(a)`, `f(a, b)`, plus the full `f(a, b, c)` — one per **trailing** default, **right-to-left**. Pros: zero boilerplate, Kotlin/Java parity, defaults stay single-sourced. Cons: - You **cannot customize** an individual overload's behavior. - It only generates **contiguous-from-the-right** combinations, not every subset (Kotlin's named-argument omission of a *middle* parameter has no Java equivalent overload). ### Manual overloads Write each Java-facing overload yourself, delegating to the full method. Pros: full control, can vary behavior/JavaDoc per overload. Cons: boilerplate, risk of **drift** between overloads and the canonical implementation. ## Why default-parameter changes are an ABI hazard The JVM links calls by **exact descriptor** (name + parameter/return types). The compiler bakes into each call site either a call to the real method or to the synthetic `f$default(... , int mask, Object/DefaultConstructorMarker)`. Therefore: - **Adding a default parameter** changes the **real** signature *and* the `$default` signature (one more param, possibly more mask `int`s). Code compiled against the **old** descriptors still references them; at runtime the new classfile lacks the old `$default` descriptor → **`NoSuchMethodError`**, even though re-compiling the source works fine. - **Reordering parameters** reassigns **bitmask bits** and changes overload shapes — semantically and binary-wise breaking. - **Removing a default value** (keeping the parameter) can change which call sites route through `$default`. ```kotlin // v1 fun render(text: String, bold: Boolean = false) {} // v2 inserts a parameter fun render(text: String, italic: Boolean = false, bold: Boolean = false) {} // Old-compiled callers linked to v1's render$default descriptor break at runtime. ``` ## Practical guidance for library authors - Treat **default-parameter changes as binary-breaking**; bump the major version and require consumer recompilation. - Prefer **append-only, trailing** new parameters and accept you still need a recompile for binary consumers. - Decide **@JvmOverloads vs manual** up front; adding @JvmOverloads later *adds* methods (source-compatible) but is itself a method-table change. - For stable cross-language APIs, some teams avoid defaults entirely on the public surface and expose explicit overloads or a builder. ## Key terms - **ABI / binary compatibility**: ability of already-compiled callers to link without recompilation. - **Descriptor**: the JVM's exact method signature used for linking. - **`NoSuchMethodError`**: thrown when a linked descriptor no longer exists at runtime.
- Is adding @JvmOverloads to an existing function source-compatible? Binary-compatible?Source-compatible (it only adds methods). Not strictly binary-neutral: it changes the class's method set, though existing callers' linked descriptors still exist, so existing calls keep working.
- How can a library expose defaults to Java without ABI fragility?Avoid defaults on the public surface: provide explicit, append-only overloads or a builder/DSL, so signatures stay stable and additions are purely additive.
The bitmask and $default descriptor are a wiring diagram baked into every caller; rearranging the pins (parameters) silently breaks every device wired to the old diagram.
saying these in an interview costs you the question
- Claiming default-parameter changes are always safe because source still compiles
- Thinking @JvmOverloads generates every subset of arguments
- Ignoring NoSuchMethodError as a real runtime outcome
- Treating reordering parameters as harmless
- No awareness of ABI vs source compatibility distinction