skip to content

When and why would you use @JvmName on an individual extension function (not the whole file), and what interop problem does it solve?

level: seniorimportance: should knowfreq 35%

answer

  1. Generics erase → same JVM signature → clash
  2. @JvmName on fun renames one emitted method
  3. Kotlin call sites unaffected; Java uses new name
  4. Function-level cousin of @file:JvmName
  5. Also fixes Java-illegal/ugly names

basics

~20 s

Generic types erase at runtime, so two Kotlin extensions that look distinct can compile to the same JVM signature and clash. Putting @JvmName on one of them gives it a different method name so both can coexist.

solid answer

~40 s

Because of **type erasure**, generic extensions like `fun List<Int>.sum()` and `fun List<String>.sum()` both erase to `static X sum(List)` on the JVM — same signature, a 'platform declaration clash' compile error. Annotating a function with `@JvmName("sumInts")` renames just that emitted method so the JVM signatures differ, resolving the clash and giving Java a usable name. Kotlin call sites are unaffected (they still call `sum()`); only the JVM/Java-visible name changes. This is the function-level cousin of `@file:JvmName`: `@file:JvmName` renames the facade class, while `@JvmName` on a function renames one method. It is also used to expose a Kotlin name that would be illegal or ugly in Java.

code

kotlin · 6 lines
kotlin
fun List<Int>.sumIt(): Int = sum()

@JvmName("sumDoubles")
fun List<Double>.sumIt(): Double = sum()
// Without @JvmName: Platform declaration clash (both erase to sumIt(List))
// Kotlin: list.sumIt()   Java: CollKt.sumDoubles(list)

go deeper

for a junior

Recognizes that two similar extensions can conflict and that an annotation can rename one.

for a middle

Explains the clash but may be fuzzy on why erasure causes identical signatures.

for a senior

Ties it precisely to erasure, shows @JvmName fixing the JVM signature while Kotlin call sites stay unchanged, and contrasts with @file:JvmName.

for a principal

Manages these renames as part of a stable Java-facing API, anticipating erasure clashes in library design and documenting the Java method names.

## The erasure clash Kotlin keeps full generic types at compile time, but the JVM **erases** them. So these two perfectly valid Kotlin extensions: ```kotlin fun List<Int>.average(): Double = ... fun List<Double>.average(): Double = ... ``` both erase to `static double average(List $receiver)` — **identical JVM signatures**. The compiler reports a *"Platform declaration clash"* / *"conflicting overloads"* error because the JVM cannot hold two methods with the same signature. ## The fix: @JvmName on the function ```kotlin fun List<Int>.average(): Double = ... @JvmName("averageOfDoubles") fun List<Double>.average(): Double = ... ``` Now the second compiles to `static double averageOfDoubles(List)`. The signatures differ, so both coexist. ## Effect on call sites - **Kotlin** callers still write `myDoubles.average()`; resolution uses the *static Kotlin types*, so the right one is picked. The `@JvmName` is invisible to Kotlin. - **Java** callers must use the renamed method: `CollKt.averageOfDoubles(list)`. ## Other uses of @JvmName on a function - Expose a different Java name (e.g. the Kotlin name collides with a Java keyword or convention): `@JvmName("isEmpty")`. - Rename a getter/setter on a property. ## Relationship to @file:JvmName | Annotation | Renames | Target | |---|---|---| | `@file:JvmName("X")` | the facade **class** | whole file | | `@JvmName("x")` on fun | one **method** | single function | ## Pitfalls - `@JvmName` cannot be applied to `open`/`override` members that participate in virtual dispatch in some cases — but extensions are static, so this is usually fine. - Renaming changes the **Java-visible** binary contract; treat it as API. - Two same-signature extensions are still ambiguous to Java if both are reachable — give each a distinct `@JvmName` if Java must call both.

  • Do Kotlin callers need to change when you add @JvmName to one of the clashing extensions?
    No. Kotlin resolution uses the static types and ignores @JvmName; only the Java-visible method name changes.
  • What error do you get without @JvmName for two erasure-identical extensions?
    A 'Platform declaration clash' / conflicting JVM declarations compile error.

Two siblings with the same legal name confuse the post office (JVM); @JvmName gives one a nickname so mail (calls) reaches the right person.

saying these in an interview costs you the question

  • Doesn't connect the clash to JVM type erasure
  • Thinks @JvmName changes which extension Kotlin resolves
  • Confuses @JvmName (one method) with @file:JvmName (the facade class)
  • Believes the clash is a runtime, not compile-time, problem

context