skip to content

A Kotlin function with default parameter values shows up in Java as both a normal method and a `name$default(...)` method with extra int and Object arguments. Explain what the `$default` synthetic method is and how a Java caller should use it.

level: middleimportance: should knowfreq 41%

answer

  1. Defaults -> real method + name$default
  2. Extra int bitmask = which args omitted
  3. Trailing Object marker (usually null)
  4. @JvmOverloads -> real telescoping overloads
  5. $default is synthetic, not for humans

basics

~20 s

Java has no default arguments, so Kotlin compiles a function with defaults into the real method plus a synthetic $default helper. The helper takes an extra bitmask telling it which arguments were omitted, then fills in the defaults. Java should normally use @JvmOverloads instead.

solid answer

~40 s

For `fun f(a: Int, b: Int = 2, c: Int = 3)` Kotlin emits the real `f(int,int,int)` plus a synthetic `f$default(int a, int b, int c, int mask, Object marker)`. The `mask` is a bitmask where bit N set means 'argument N was not supplied, use its default'; the trailing `Object` is an unused marker for overload disambiguation. Kotlin callers omitting args route through `f$default` with the right mask. Java can't write defaults, so by default it must call `f$default(a, 0, 0, 0b110, null)` — ugly and fragile. The idiomatic fix is `@JvmOverloads`, which makes the compiler also generate real telescoping overloads `f(a)`, `f(a,b)`, `f(a,b,c)`, so Java calls them naturally and never touches `$default`. The `$default` method is synthetic and not intended for direct human use.

code

kotlin · 8 lines
kotlin
@JvmOverloads
fun connect(host: String, port: Int = 443, tls: Boolean = true) { /* ... */ }
// Without @JvmOverloads, Java must do:
//   connect$default("h", 0, false, 0b110, null);
// With @JvmOverloads, Java can do:
//   connect("h");
//   connect("h", 8080);
//   connect("h", 8080, false);

go deeper

for a junior

Knows defaults aren't a JVM feature and that @JvmOverloads makes Java-friendly overloads.

for a middle

Explains the $default dispatcher with bitmask + marker and that set bit = omitted.

for a senior

Details prefix-only overload generation, constructor DefaultConstructorMarker, reflection clutter, and API design guidance.

for a principal

Designs Kotlin-from-Java APIs deliberately: parameter ordering for useful overloads, when defaults beat overloads, ABI stability of adding defaults.

## Why `$default` exists The JVM has **no default parameter values**. Kotlin supports them, so for any function/constructor with at least one default it generates two things: 1. The **real method** with the full parameter list, e.g. `f(int, int, int)`. 2. A **synthetic dispatcher** `f$default(...)` that adds two trailing parameters: an `int` (or several ints if >32 params) **bitmask** and an `Object` **marker**. ```kotlin fun greet(name: String, greeting: String = "Hello", loud: Boolean = false): String = (if (loud) greeting.uppercase() else greeting) + ", " + name ``` Generated (conceptually): ```java public static String greet(String name, String greeting, boolean loud) { ... } // real // synthetic dispatcher: public static String greet$default(String name, String greeting, boolean loud, int mask, Object marker) { if ((mask & 2) != 0) greeting = "Hello"; // bit 1 -> param index 1 omitted if ((mask & 4) != 0) loud = false; // bit 2 -> param index 2 omitted return greet(name, greeting, loud); } ``` ## The bitmask - Each parameter has a bit by position (param index 0 -> bit 0, index 1 -> bit 1, ...). - A set bit means **'this argument was omitted; substitute the default expression.'** - With more than 32 parameters, multiple int masks are appended. - The trailing `Object` marker is normally `null`; it exists to keep the synthetic signature distinct and is used by the runtime for open/override dispatch of defaults. ## How callers reach it - **Kotlin** code calling `greet("Sam")` is rewritten by the compiler into `greet$default("Sam", null, false, 0b110, null)`. You never see this. - **Java** code, lacking defaults, would otherwise have to call the synthetic method manually with a hand-computed mask — brittle and unreadable. ## The interop fix: `@JvmOverloads` Annotate the function with `@JvmOverloads` and the compiler **also** emits real telescoping overloads: ```kotlin @JvmOverloads fun greet(name: String, greeting: String = "Hello", loud: Boolean = false): String = ... // Java now sees: greet(name), greet(name, greeting), greet(name, greeting, loud) ``` Java calls `greet("Sam")` naturally; it never references `greet$default`. This is the recommended approach for any Kotlin API meant to be consumed from Java. ## Gotchas - `$default` methods clutter Java completion and reflection; treat them as implementation detail. - `@JvmOverloads` only generates the *prefix* overloads (dropping trailing parameters), not arbitrary combinations, so the order of parameters matters for which Java-friendly overloads you get. - Constructors with defaults get a `$default`-style synthetic constructor too (plus an extra `DefaultConstructorMarker`).

  • What does the bit being SET in the mask mean — supplied or omitted?
    Set means the argument was OMITTED, so the dispatcher substitutes that parameter's default expression.
  • Why does @JvmOverloads not solve every Java ergonomics case?
    It only generates prefix overloads (dropping trailing params), so you can't get arbitrary subsets; parameter ordering dictates which Java-callable combinations exist.

$default is a fill-in clerk: you hand it your form with blanks plus a checklist (bitmask) of which blanks to auto-fill, and it completes the missing fields before submitting.

saying these in an interview costs you the question

  • Saying a set mask bit means the argument WAS supplied
  • Claiming Java can call defaults without @JvmOverloads cleanly
  • Thinking @JvmOverloads generates every combination of overloads
  • Not knowing the trailing Object marker exists
  • Believing defaults are a JVM feature

context