How does Java code call a Kotlin top-level function, and what interop pitfalls should you anticipate?
answer
- Java: FileNameKt.fn(args) static call
- Renaming the .kt file breaks Java callers — pin with @file:JvmName
- internal is name-mangled — not Java-callable
- @JvmOverloads for default args; suspend/reified don't map cleanly
- Extension receiver becomes the first Java parameter
basics
~10 sJava calls the function as a static method on the generated file class, like FileNameKt.functionName(args). The function name and the 'Kt' suffix matter, and Kotlin-only features may not map cleanly.
solid answer
~30 sBecause a top-level function is emitted as a `public static` method on the `<FileName>Kt` facade, Java invokes it as `FileNameKt.functionName(args)` (importing `com.pkg.FileNameKt` or using the FQN). Pitfalls: (1) renaming or moving the `.kt` file changes the facade class name and **breaks** Java callers; use `@file:JvmName` to pin a stable name. (2) `internal` top-level functions are name-mangled, so they're effectively un-callable from Java. (3) Kotlin-only features need bridges: default arguments require `@JvmOverloads` to generate Java-friendly overloads, and `inline`/`reified` functions and `suspend` functions don't have clean Java signatures. (4) Extension functions appear to Java as static methods whose first parameter is the receiver.
code
kotlin · 10 lines// Kotlin — File: StringUtils.kt
@file:JvmName("StringUtils") // stable Java name
package com.example
@JvmOverloads
fun pad(s: String, width: Int = 8): String = s.padStart(width)
// Java:
// StringUtils.pad("x"); // uses default width via @JvmOverloads
// StringUtils.pad("x", 10);go deeper
Can call FileNameKt.fn from Java when told the class name but may not anticipate pitfalls.
Knows the static facade call, @JvmOverloads for defaults, and that file renames break callers.
Anticipates internal mangling, suspend/reified non-mapping, and pins facade names for stable interop.
Designs the Kotlin↔Java boundary deliberately: stable @JvmName facades, overload strategy, and what to keep out of the Java surface.
## The basic call shape A Kotlin top-level `fun shout(s: String)` in `StringUtils.kt` (package `com.example`) is, to Java, a static method on `com.example.StringUtilsKt`: ```java import com.example.StringUtilsKt; String r = StringUtilsKt.shout("hi"); ``` Or fully qualified: `com.example.StringUtilsKt.shout("hi")`. ## Pitfall 1 — fragile facade name The facade name is derived from the **file name**. Renaming `StringUtils.kt` to `Strings.kt` silently changes the Java-visible class from `StringUtilsKt` to `StringsKt`, breaking every Java caller. **Fix:** pin it with a file-level annotation: ```kotlin @file:JvmName("StringUtils") package com.example ``` Now the class is `StringUtils` regardless of the file name. ## Pitfall 2 — internal is mangled An `internal` top-level function is compiled as `public static` but with a **name-mangled** suffix to discourage cross-module Java calls. In practice Java cannot reliably call it. Keep Java-facing functions `public`. ## Pitfall 3 — Kotlin-only features need bridges - **Default arguments:** Java has no defaults, so `fun f(a: Int, b: Int = 0)` exposes only the full-arity method. Add `@JvmOverloads` to generate the overload `f(a)` for Java. - **suspend functions:** carry a hidden `Continuation` parameter and are impractical to call directly from plain Java. - **inline / reified:** inlining happens at Kotlin call sites; Java cannot use `reified` type parameters. - **Vararg / Unit:** `vararg` maps to a Java array param; `Unit`-returning functions map to `void`. ## Pitfall 4 — extension functions A top-level extension `fun String.shout()` becomes a static method whose **first parameter is the receiver**: Java calls `StringUtilsKt.shout("hi")` passing the string explicitly. ## How to inspect Use `javap -p com.example.StringUtilsKt` or IntelliJ's **Show Kotlin Bytecode ▸ Decompile** to see the exact Java-visible signatures before relying on them.
- Why does a Kotlin default argument not 'just work' from Java?Java has no default parameters; the compiler emits only the full-arity method unless you add @JvmOverloads to generate reduced-arity overloads.
- How would you stop a file rename from breaking Java callers?Annotate the file with @file:JvmName("FixedName") so the facade class name is decoupled from the file name.
saying these in an interview costs you the question
- Calling the function in Java without the 'Kt' (or @JvmName) class qualifier
- Expecting Kotlin default arguments to appear as Java overloads without @JvmOverloads
- Assuming internal functions are callable from Java
- Thinking suspend functions have a plain Java signature
- Forgetting the extension receiver becomes the first Java parameter