How do default arguments in a Kotlin primary constructor work, and how do they interact with the JVM and named arguments?
answer
- Default arg = no value needed at call site
- Named args skip middle/earlier params
- JVM uses bitmask + $default, not overloads
- @JvmOverloads for Java callers
- Defaults can reference earlier params
basics
~10 sYou can give constructor parameters default values. Callers can leave those out, and Kotlin fills in the defaults. Named arguments let you skip earlier parameters while still setting later ones.
solid answer
~40 sDefault arguments (`val port: Int = 8080`) let one constructor replace many telescoping overloads. At the call site you combine them with **named arguments** to skip middle parameters: `Server(port = 9090)`. On the JVM Kotlin does not emit a Java overload per default; instead it compiles a single synthetic constructor plus a bitmask that records which arguments were supplied — so Java callers cannot use the Kotlin defaults unless you add `@JvmOverloads`, which generates the overload set. Defaults are evaluated at call time and may reference earlier parameters (`class Box(val w: Int, val h: Int = w)`). Be careful with mutable default values: each call gets a fresh evaluation, but if the default is a shared mutable object reference you can leak state.
code
kotlin · 5 linesclass Box(val width: Int, val height: Int = width, val depth: Int = 1)
Box(4) // 4 x 4 x 1
Box(4, depth = 3) // 4 x 4 x 3, named arg skips height
Box(width = 2, height = 5)go deeper
Knows you can give parameters defaults and omit them when constructing.
Combines defaults with named arguments and knows reordering risks.
Explains the JVM bitmask/$default mechanism and the @JvmOverloads interop fix.
Weighs defaults vs builders/factories for public APIs and ABI stability across versions.
## Declaring defaults Any primary-constructor parameter can carry a default expression: ```kotlin class Server( val host: String = "localhost", val port: Int = 8080, val tls: Boolean = false, ) ``` This eliminates the Java-style **telescoping constructor** problem (many overloads with growing parameter lists). ## Named arguments unlock partial calls Because Kotlin matches arguments by position by default, to skip an earlier defaulted parameter you must name the later one: ```kotlin Server() // all defaults Server("db1") // host only Server(port = 9090) // skip host, set port by name Server(host = "db1", tls = true) ``` ## Defaults can depend on earlier parameters A default expression may reference parameters declared to its left (and any accessible members): ```kotlin class Box(val width: Int, val height: Int = width) ``` ## JVM / interop reality Kotlin does **not** generate one constructor per default. It compiles: - the full constructor, plus - a **synthetic `$default` constructor** taking an extra `int` **bitmask** marking which params were omitted. The Kotlin compiler injects the right mask at each call site. Consequences: - **Java callers** cannot use Kotlin defaults directly. Add `@JvmOverloads` to the constructor to generate a real overload for each defaulted tail parameter: ```kotlin class Server @JvmOverloads constructor( val host: String = "localhost", val port: Int = 8080, ) ``` - `@JvmOverloads` generates overloads only by dropping parameters from the **end**, mirroring positional rules. ## Evaluation semantics Default expressions are evaluated **at call time**, once per call where the argument is omitted. A default like `= mutableListOf()` therefore produces a fresh list each call — but a default referencing a captured shared object shares that object across calls. ## Pitfalls - Reordering parameters silently changes which positional calls compile. - Relying on defaults across a published binary boundary requires `@JvmOverloads` or careful ABI thought.
- Why can't a Java caller use Kotlin constructor defaults out of the box?Kotlin compiles a single synthetic constructor with a bitmask, not Java overloads. `@JvmOverloads` makes Kotlin emit the actual overload set Java needs.
- Can a default value reference another constructor parameter?Yes, as long as that parameter is declared earlier (to the left) in the header.
saying these in an interview costs you the question
- Believing Kotlin emits one Java overload per default automatically
- Trying to skip a positional parameter without naming the later one
- Saying defaults are evaluated once at class-load time
- Not knowing @JvmOverloads exists for Java interop
- Claiming a default can reference a parameter declared after it