skip to content

Show how to resolve a generic-erasure platform clash for two overloads using @JvmName, and explain what each annotation changes.

level: middleimportance: must knowfreq 50%

answer

  1. Rename at least one side with @JvmName
  2. Kotlin name unchanged; Java sees new name
  3. Use @get:/@set:JvmName for accessors
  4. Cannot rename open/override members
  5. @file:JvmName renames the facade class

basics

~10 s

Put @JvmName with a different name on at least one of the clashing functions. That gives each a unique method name in the compiled code, so the JVM no longer sees a duplicate.

solid answer

~40 s

Annotate the clashing declarations with `@JvmName("uniqueName")` so each emits a distinct JVM method name. You only strictly need to rename one of the two, but renaming both makes the Java-facing API clearer. `@JvmName` targets functions, property getters/setters (via use-site targets `@get:JvmName`/`@set:JvmName`), and file facade classes (`@file:JvmName`). It changes only the generated bytecode symbol; Kotlin still resolves the original Kotlin name, so Kotlin call sites are untouched, while Java callers must use the new name. A caveat: `@JvmName` cannot be applied to `open`/`override` members participating in inheritance, because renaming would break the virtual method table — so you cannot rename overrides this way.

code

kotlin · 8 lines
kotlin
@JvmName("filterInts")
fun filter(xs: List<Int>): List<Int> = xs.filter { it > 0 }

@JvmName("filterStrings")
fun filter(xs: List<String>): List<String> = xs.filter { it.isNotEmpty() }

// Kotlin: filter(listOf(1,-2)) / filter(listOf("a",""))
// Java:   filterInts(...) / filterStrings(...)

go deeper

for a junior

Knows @JvmName fixes the clash and can copy the pattern.

for a middle

Applies it to one or both sides, explains Kotlin-vs-Java visibility, and uses accessor targets.

for a senior

Knows the override restriction and chooses between @JvmName and distinct Kotlin names per API audience.

for a principal

Designs the public surface so Java consumers get clean, documented names and avoids needing the workaround.

## The setup Generic erasure collapses these to one descriptor: ```kotlin fun ids(xs: List<Int>): String = xs.joinToString() // ids(List) fun ids(xs: List<String>): String = xs.joinToString() // ids(List) -> CLASH ``` ## The fix ```kotlin @JvmName("idsFromInts") fun ids(xs: List<Int>): String = xs.joinToString() @JvmName("idsFromStrings") fun ids(xs: List<String>): String = xs.joinToString() ``` Result in bytecode: `idsFromInts(List)` and `idsFromStrings(List)` — distinct descriptors. From **Kotlin** you call `ids(listOf(1,2))` and `ids(listOf("a"))` exactly as before. From **Java** you call `idsFromInts(...)` / `idsFromStrings(...)`. ## What @JvmName can target - **Top-level / member functions**: `@JvmName("...")`. - **Property accessors** via use-site targets: `@get:JvmName("...")`, `@set:JvmName("...")` — useful when a generated getter collides. - **File facade class**: `@file:JvmName("Utils")` at the top of a file renames the synthetic `XxxKt` class. ## Important caveats - You can rename just **one** side and the clash disappears, but renaming both yields a self-documenting Java API. - `@JvmName` is **forbidden on `open`, `abstract`, and `override` members** that take part in virtual dispatch — renaming would corrupt overriding, so the compiler rejects it. Restructure (e.g. delegate to differently named private helpers) instead. - It does **not** change overload resolution within Kotlin; the Kotlin name is what Kotlin sees. - Alternative when you don't need both names visible: simply give the functions different **Kotlin** names — no annotation needed. ## Mental model Think of `@JvmName` as a label printer for the bytecode: it swaps the sticker the JVM reads while leaving the Kotlin-side name intact.

  • Can you put @JvmName on an overriding function to dodge a clash?
    No. The compiler forbids @JvmName on open/abstract/override members because it would break virtual dispatch. Refactor to non-virtual helpers or give distinct Kotlin names instead.
  • Is renaming only one of the two functions enough?
    Yes — once the descriptors differ, the clash is gone. Renaming both is just clearer for Java consumers.

saying these in an interview costs you the question

  • Believing @JvmName changes Kotlin-side resolution
  • Trying to apply @JvmName to an override and assuming it compiles
  • Forgetting accessors need @get:/@set: use-site targets
  • Claiming you must annotate both functions

context