Explain named and default arguments in Kotlin. How do they reduce overloads, and what interop/ordering rules must you keep in mind?
answer
- Default = fallback in signature
- Named = pass by parameter name
- Collapses telescoping overloads to one fn
- Defaults evaluated at call site
- @JvmOverloads for Java to see them
basics
~20 sA default argument gives a parameter a value used when the caller omits it. A named argument lets the caller pass values by parameter name instead of position. Together they remove the need for many overloaded versions of the same function.
solid answer
~40 s**Default arguments** assign a fallback in the signature: `fun connect(host: String, port: Int = 443, tls: Boolean = true)`. Callers can skip trailing ones. **Named arguments** pass values by name — `connect("x", tls = false)` — so you can skip a middle default and stay readable. Together they collapse the Java 'telescoping overloads' pattern into one function. Rules: positional args must come before named ones (until all remaining are named); once you name one argument out of order, the rest typically must be named too; defaults are evaluated at the **call site** per call. Interop: Java callers don't see named/default args — you must add `@JvmOverloads` to make the compiler generate the overload chain, otherwise Java only sees the full-arity method. Defaults can reference earlier parameters but be careful with side-effecting defaults.
code
kotlin · 11 lines@JvmOverloads
fun request(
url: String,
method: String = "GET",
retries: Int = 3,
timeoutMs: Long = 5_000,
) { /* ... */ }
request("/a") // all defaults
request("/b", retries = 0) // skip method, set retries by name
request("/c", "POST", timeoutMs = 10_000)go deeper
Can declare a default and call with a named argument.
Explains how defaults+named replace telescoping overloads and the call-site evaluation rule.
Knows the ordering rules and the @JvmOverloads interop requirement and its generated chain.
Designs APIs with parameter ordering, binary-compatibility of adding/reordering defaults, and side-effect-free defaults for library evolution.
## Default arguments A **default argument** supplies a value used when the caller omits that parameter: ```kotlin fun connect(host: String, port: Int = 443, useTls: Boolean = true) { /* ... */ } connect("example.com") // port=443, useTls=true connect("example.com", 8080) // useTls=true ``` The default expression is evaluated **at the call site** each time the parameter is omitted, so `fun log(t: Long = System.currentTimeMillis())` gets a fresh value per call. ## Named arguments A **named argument** passes a value by the parameter's name rather than its position: ```kotlin connect("example.com", useTls = false) // skip the middle default, set the last ``` Naming improves call-site readability (especially booleans/numbers whose meaning isn't obvious) and lets you **skip a middle default** without passing it. ## Why this kills overload explosions Java's classic workaround is **telescoping constructors/overloads**: ```java void connect(String h) { connect(h, 443, true); } void connect(String h, int p) { connect(h, p, true); } void connect(String h, int p, boolean t) { ... } ``` Kotlin replaces all of these with **one** function plus defaults. Fewer methods, one source of truth. ## Ordering rules - Positional arguments must appear **before** named ones — but once you start naming, you may keep using positions only if they still line up. The safe rule: after the first named argument, name the rest. - You can reorder arguments **only** when you name them. - A parameter without a default that follows ones with defaults can only be supplied by name if you skipped earlier defaults. ## Java interop — the gotcha Named and default arguments are a **Kotlin compile-time** feature; the bytecode has one method with all parameters. So **Java callers must pass every argument** — they get no defaults. To expose the overload chain to Java, annotate with `@JvmOverloads`: ```kotlin @JvmOverloads fun connect(host: String, port: Int = 443, useTls: Boolean = true) { /* ... */ } ``` This makes the compiler generate `connect(host)`, `connect(host, port)`, and `connect(host, port, useTls)` for Java. ## Practical tips - Put parameters most likely to be defaulted **last**. - Use named args at call sites for clarity even when not required. - Avoid heavy/side-effecting default expressions; remember they run per omitted call.
- Why does a Java caller not get Kotlin default arguments by default?The bytecode is a single full-arity method; defaults are resolved by the Kotlin compiler at the call site. Add @JvmOverloads to generate the overload chain for Java.
- When are default-argument expressions evaluated?At the call site, each time the parameter is omitted — so a default like System.currentTimeMillis() yields a fresh value per call.
Like an online order form: blank fields use sensible defaults, and you can fill in just the one box you care about by its label instead of guessing its position.
saying these in an interview costs you the question
- Thinking defaults work automatically from Java without @JvmOverloads
- Believing the default is evaluated once and cached
- Reordering positional args without naming them
- Claiming named args change the bytecode signature
- Saying you still need overloads in Kotlin for optional params