Explain how Kotlin `vararg` maps to JVM varargs for interop, how spread interacts with calling Java varargs methods, and one subtlety of overload resolution between a vararg overload and a fixed-arity overload.
answer
- vararg -> JVM T[] with ACC_VARARGS flag
- Java may pass array directly; Kotlin needs *
- Fixed-arity overload beats vararg (more specific)
- Generic vararg erases to Object[]
- Reified needed for real runtime element type
basics
~20 sKotlin vararg compiles to a normal JVM varargs array, so Java code can call it and Kotlin can call Java varargs the same way. To pass an existing array to either, use the * spread. When both a vararg and an exact fixed overload match, Kotlin prefers the more specific fixed-arity one.
solid answer
~40 sA Kotlin `vararg t: T` compiles to a JVM method taking `T[]` (an array) with the ACC_VARARGS flag, so it is callable from Java as `m(a, b, c)` and from Kotlin as usual. Calling a **Java** varargs method from Kotlin works symmetrically: pass comma-separated args, or spread an existing array with `*arr`. Java permits passing an array directly to a varargs slot without any spread; **Kotlin requires the explicit `*`** even for Java methods. For overload resolution, Kotlin's rules prefer the **most specific applicable** signature: a fixed-arity overload that matches the exact argument count and types is chosen over a `vararg` overload, because varargs candidates are considered less specific. This mirrors Java. Generic `vararg t: T` where `T` is a type parameter compiles to `Object[]` after erasure, which affects reified/array-of-generic situations.
code
kotlin · 10 linesfun f(a: Int) = "fixed"
fun f(vararg a: Int) = "vararg"
fun main() {
println(f(1)) // fixed (most specific)
println(f(1, 2)) // vararg
println(f()) // vararg (fixed needs 1 arg)
val arr = intArrayOf(3, 4)
println(f(*arr)) // vararg via spread
}go deeper
May know Kotlin vararg maps to an array but not interop/overload details.
Knows Java interop uses arrays and that Kotlin needs * to spread into Java varargs.
Explains ACC_VARARGS mapping, fixed-vs-vararg specificity, and the Kotlin-vs-Java spread asymmetry.
Adds erasure-to-Object[] and reification, allocation/ABI trade-offs, and API guidance (vararg vs Collection).
## JVM mapping Kotlin `fun m(vararg t: T)` compiles to a JVM method whose last parameter is an array `T[]` and which carries the **`ACC_VARARGS`** flag. Concretely: - `vararg s: String` → `m(String[])` - `vararg x: Int` → `m(int[])` (primitive array, no boxing) - `vararg t: T` with type parameter `T` → erases to `m(Object[])` Because of the varargs flag, **Java callers** can write `m("a", "b")` naturally. ## Calling Java varargs from Kotlin Kotlin can call any Java varargs method. Two ways to pass an existing array: ```kotlin val parts = arrayOf("a", "b") String.format("%s %s", *parts) // spread required in Kotlin ``` Key asymmetry: **Java** lets you pass an array directly into a varargs slot (`format(fmt, arr)`), but **Kotlin mandates the explicit `*` spread**. Forgetting it gives a type error (you would be passing one `Array<String>` where the element type is expected). For `Array<out Any?>`/`Object...` cases, watch for the classic nested-array ambiguity — spreading an `Object[]` whose single element is itself an array. ## Overload resolution: fixed vs vararg Kotlin chooses the **most specific** applicable candidate: ```kotlin fun f(a: Int) = "fixed" fun f(vararg a: Int) = "vararg" f(1) // "fixed" — exact fixed-arity wins f(1, 2) // "vararg" — only the vararg form applies f() // "vararg" — fixed needs one arg ``` The fixed-arity overload is preferred for `f(1)` because a vararg candidate is treated as **less specific**. This matches Java's phase ordering (fixed-arity applicability is considered before variable-arity). It means adding a vararg overload will not silently steal calls that an exact overload already satisfies. ## `@JvmStatic`/named-arg subtleties - If a `vararg` is not last, callers must use **named arguments** for trailing params; but the spread/vararg JVM signature still puts the array in declaration order. - Spreading an empty array (`*emptyArray()`) is valid and yields zero arguments. ## Generics and erasure Because `vararg t: T` erases to `Object[]`, you cannot rely on the runtime element type; combine with `reified` type parameters in `inline` functions if you need the actual `Class<T>` for array creation (`arrayOfNulls<T>`-style needs reification). ## Practical guidance - Prefer accepting a `List`/`Collection` for large or already-collection data to avoid spread copies; reserve `vararg` for ergonomic small fixed-prefix call sites. - Remember Kotlin's mandatory `*` when migrating Java code that passed arrays to varargs directly.
- Why must Kotlin use `*` to pass an array to a Java varargs method when Java doesn't?Kotlin's type system treats the array as a single value; the explicit spread operator is the only way to ask the compiler to expand it into individual varargs arguments, removing the array-vs-element ambiguity.
- What does `vararg t: T` (type parameter T) erase to on the JVM?`Object[]` — the element type is erased, so runtime element-type info requires a reified type parameter on an inline function.
saying these in an interview costs you the question
- Saying Kotlin can pass an array to varargs without `*` like Java
- Claiming the vararg overload is chosen over an exact fixed overload
- Thinking primitive varargs box into Object[]
- Asserting generic vararg keeps its runtime element type without reification
- Believing Java cannot call Kotlin vararg methods naturally