skip to content

How does a Java caller invoke a Kotlin extension function like `fun String.shout(): String`, and how are the receiver and any parameters passed?

level: middleimportance: must knowfreq 60%

answer

  1. Receiver becomes the FIRST static param
  2. FacadeKt.fn(receiver, args...) from Java
  3. Ext property → static getX(receiver)
  4. @JvmOverloads to expose defaults to Java
  5. Nullable receiver = Java may pass null

basics

~10 s

From Java, the extension is a static method on the file's facade class. You pass the receiver as the first argument: instead of 'hi'.shout() you write StringUtilsKt.shout("hi"). Extra parameters follow the receiver.

solid answer

~30 s

A Kotlin extension `fun String.shout(prefix: String): String` compiles to a `public static` method on the facade class whose **first parameter is the receiver** and whose remaining parameters follow in order: `static String shout(String $receiver, String prefix)`. Java has no `this`-style extension syntax, so the call is `StringUtilsKt.shout("hi", ">")`. Rename the facade with `@file:JvmName` for readability. Extension **properties** become a static getter (`getFoo($receiver)`) since there is no backing field. Default parameter values are not visible to Java unless you add `@JvmOverloads`. The receiver argument is non-null for non-nullable receiver types; a nullable receiver (`String?.x`) just means Java may pass null.

code

kotlin · 9 lines
kotlin
// Kotlin
package com.app
fun String.shout(prefix: String) = prefix + uppercase() + "!"
val String.lastChar get() = this[length - 1]

/* Java:
   StringUtilsKt.shout("hi", ">");        // ">HI!"
   StringUtilsKt.getLastChar("abc");      // 'c'
*/

go deeper

for a junior

Knows Java calls the extension as a static method with the receiver passed in.

for a middle

States the receiver is the first parameter and locates the facade class; knows extension properties become getters.

for a senior

Covers @JvmOverloads for defaults, nullability of the receiver, and internal-visibility mangling.

for a principal

Designs the Kotlin extension surface to be ergonomic and binary-stable for Java consumers, choosing facade names and @JvmOverloads policy deliberately.

## The core transformation Extensions are **static functions with the receiver as the first parameter**. Kotlin's `receiver.fn(args)` is sugar; on the JVM bytecode there is just `fn(receiver, args)`. Given: ```kotlin // file: StringUtils.kt package com.app fun String.shout(prefix: String): String = prefix + uppercase() + "!" ``` the compiler emits roughly: ```java public final class StringUtilsKt { public static String shout(String $receiver, String prefix) { ... } } ``` ## Calling from Java ```java String r = StringUtilsKt.shout("hi", ">"); // ">HI!" ``` The **receiver goes first**, then declared parameters in order. With `@file:JvmName("StringUtils")` the call reads `StringUtils.shout("hi", ">")`. ## Extension properties An extension property has **no backing field**, so it compiles to a static accessor: ```kotlin val String.lastChar: Char get() = this[length - 1] ``` → `public static char getLastChar(String $receiver)`. Java: `StringUtilsKt.getLastChar("abc")`. ## Default values & @JvmOverloads Kotlin default parameter values are a Kotlin-only feature. Java sees a single method requiring all arguments unless you annotate the function with `@JvmOverloads`, which generates overloads omitting trailing defaulted params. ## Nullability - Non-nullable receiver `String` → Java passes a non-null `String` (Kotlin may add a null check). - Nullable receiver `String?` → the method accepts `null` for `$receiver`; inside, `this` may be null. ## Other interop notes - `@JvmStatic` does **not** apply to top-level functions (they are already static); it is for companion/object members. - Internal-visibility extensions get name-mangled and are effectively not callable from Java. - `inline` extensions are still emitted as regular static methods for Java callers (Java cannot inline them).

  • How do you make a Kotlin extension's default parameter values usable from Java?
    Annotate the function with @JvmOverloads so the compiler generates overloads that omit the defaulted trailing parameters.
  • How does a Kotlin extension PROPERTY appear to Java?
    As a static getter taking the receiver, e.g. getLastChar(String $receiver); there is no field because extension properties have no backing field.

saying these in an interview costs you the question

  • Thinks Java can use receiver-dot syntax like "hi".shout()
  • Forgets the receiver is passed as the first argument
  • Claims default parameter values just work from Java without @JvmOverloads
  • Says extension properties expose a field to Java

context