Sooner or later you write a function and can’t say how many arguments it’ll get. A logger might get one detail or six. A query helper might get none. Kotlin handles this with the vararg modifier on a single parameter. The caller passes as many values of that type as they want, and there’s no need to wrap them in an array first.
Mobile codebases use it all the time, mostly in logging calls and query builders. The feature itself hasn’t moved much either: arrayOf() has had the same vararg parameter since Kotlin’s first stable release in 2016, according to Kotlin’s own standard library reference (kotlinlang.org).
What Are Kotlin Varargs?

Put vararg in front of a parameter and that parameter will take zero values, one value, or as many as the caller wants, all of the same type. Inside the function body it’s just an array. From the outside, nobody had to build one.
If the language is still new to you, sort out the basics of Kotlin first. Parameter modifiers are easier to follow once the rest of the syntax feels normal.
Most people first run into varargs during Android development work, since Google lists Kotlin as its preferred language for the platform. You’ve probably used one already without noticing. listOf(1, 2, 3) and arrayOf("a", "b", "c") both run on it.
Without it, you’d be writing overloads for one argument, two arguments, five arguments. Or you’d make every caller build a list before they’re allowed to call you. Neither is fun.
Zero arguments works. Fifty works too.
How Do You Declare a Vararg Parameter in a Function Signature?

The keyword goes right before the parameter name, the same spot as any other modifier:
fun printAll(vararg messages: String)
That one line replaces a stack of overloaded functions. Kotlin’s function reference (kotlinlang.org) allows only one vararg parameter per signature, and it’s usually the last one.
I’d keep the Kotlin cheat sheet open while the rules sink in. It’s quicker than going back to the docs every time you forget where the modifier goes.
Where the Vararg Parameter Can Sit in the Parameter List
It doesn’t have to be last. It just works best there, because then callers pass arguments normally and nothing extra is needed.
Put it earlier and every parameter after it has to be passed by name. The one exception is a lambda at the very end, which can still go outside the parentheses using trailing lambda syntax.
fun greet(vararg names: String, greeting: String) forces every call to name greeting. Otherwise the compiler has no idea where the list of names stops.
Why Only One Vararg Parameter Is Allowed
Picture two of them side by side. f("a", "b", "c") could be split up in several ways, and the compiler has nothing to go on.
So Kotlin doesn’t let you try. You get a compiler error (not a warning) and the build stops right there.
What Type Does a Vararg Parameter Become Internally?

It depends on the type. A vararg of a reference type, like String, behaves as a regular Array<String> inside the function, which is how Kotlin’s own documentation describes it.
Primitives are handled differently. A vararg numbers: Int compiles to an IntArray, which is one of Kotlin’s eight dedicated primitive-type arrays, so there’s no boxing overhead. Kotlin’s array documentation (kotlinlang.org) lists the full mapping. The common ones:
- IntArray becomes Java’s
int[], which is also where a vararg of Int ends up - DoubleArray becomes
double[] - LongArray is
long[]on the Java side - BooleanArray maps to
boolean[]
There are eight in total, one per primitive type. Reference types skip all of this because object references were never boxed in the first place. If the difference between Array<Int> and IntArray still feels fuzzy, the post on Kotlin arrays goes through it.
Does it matter in a normal app? Rarely. It starts to matter in tight loops and big data sets, where boxing thousands of Ints turns into real garbage collector pressure.
How Does the Spread Operator Pass an Array Into a Vararg Parameter?

Put an asterisk in front of an array at the call site and each element goes into the vararg as if you’d typed it out yourself.
Kotlin’s documentation points to cases like building an SQL query or a formatted message. Some of the values are already sitting in an array and some you type directly into the call. You can mix the two:
fun printAllStrings(vararg strings: String)
printAllStrings("a", "b", *lettersArray)
Forget the asterisk and you’ll get a type mismatch. The array isn’t a String. It’s an array of Strings, and Kotlin won’t unwrap it for you.
Converting a List to an Array Before Spreading
Lists can’t be spread. This trips people up constantly, because a List feels close enough to an array. Here’s the fix:
- Call
.toTypedArray()on the List to get an Array of the matching type - Put the spread operator in front of the result at the call site
Primitive arrays are a small special case. An IntArray spreads straight into a vararg x: Int with no conversion needed. If the vararg is generic or typed as Any, though, you’ll need .toTypedArray() first to turn it into a boxed Array<Int>.
How Do Vararg Parameters Interact With Default Values and Named Arguments?

Named arguments show up here in two ways. Anything declared after a vararg has to be named. You can also name the vararg itself and pass it a whole array:
mergeStrings(strings = arrayOf("a", "b", "c"))
That works against fun mergeStrings(vararg strings: String). The array gets matched to the parameter by name instead of by position.
Passing Arguments After a Vararg Parameter
Once something comes after the vararg, naming it isn’t a style choice anymore. The compiler requires it.
greet("Sam", "Lee", greeting = "Hi") compiles. Leave greeting positional and it doesn’t, because “Hi” would simply be treated as a third name.
A trailing lambda is still the exception. If the last parameter has a function type, you can pass it outside the parentheses without naming it.
Vararg and Function Overload Resolution
When a fixed-arity overload and a vararg overload could both handle a call, Kotlin goes with the most specific match, and that’s the fixed one. The vararg version only gets picked when nothing else fits the argument count.
Java resolves the same conflict the same way. That’s also why adding a vararg overload next to existing functions almost never breaks the calls you already have.
When Should You Use Vararg Instead of an Array or List Parameter?
My rule of thumb: if the caller decides how many values there are right at the call site, and it’s a handful, use vararg. If the values already live in a collection somewhere, take a List.
Picking the “wrong” one rarely breaks anything. One of them usually just reads better for how the function actually gets called.
| Parameter Style | Declaration Syntax | Caller Flexibility | Typical Use Case |
|---|---|---|---|
| Vararg | vararg name: Type | Pass 0 or more values directly, no wrapping needed | Logging, formatting, small fixed-shape calls |
| Array parameter | name: Array<Type> | Caller must build the array first | Java interop, performance-sensitive code |
| List parameter | name: List<Type> | Caller passes an existing collection | Business logic operating on data already collected |
Mutability is the part most people miss. The function gets a fresh array on every vararg call (spreading copies the source array), so it can’t accidentally change the caller’s data.
A List parameter doesn’t promise that. If the caller passes a MutableList, they can still change it after your function has received it.
What Are the Advantages and Limitations of Vararg?
You pay a bit at runtime and get nicer call sites in return. How good that deal is depends on how often the function runs.
The biggest win is that the overloads go away. Callers write log("a", "b") the way they’d naturally think about it, and the signature stays the same when the usual argument count drifts from two to four over a year. That’s a whole round of code refactoring you don’t have to do on a fixed-arity function.
The costs:
- A new array gets allocated on every call, even when there are zero arguments
- Generic code boxes primitives, which undoes the primitive-array optimization from earlier
Plus the one-per-signature limit, which you’ll hit at some point.
Joshua Bloch’s Effective Java, Third Edition (Addison-Wesley, 2018) has some useful background from the Java side. Varargs were motivated in large part by printf-style formatting and by reflection’s invocation methods. Method.invoke and Constructor.newInstance were retrofitted to take a plain comma-separated argument list instead of an explicit Object[] array, even though the reflection API itself dates back to JDK 1.1, well before varargs existed. Not needing an array literal at every call site made both of those much nicer to use.
Kotlin works the same way. A function that runs once per button tap won’t notice the allocation. One that runs millions of times in a loop will.
When Does Vararg Not Apply?

It’s not the right tool when your function already gets its data as a collection. Wrapping a List through vararg just adds a conversion step and buys you nothing.
It also can’t help when a constructor needs two separate variable-length groups. You’re allowed one vararg, so neither group can use it. This happens more often than you’d think.
Generics are the quieter problem. The primitive-array trick only works for concrete types like Int or Double. A vararg of an unconstrained type T compiles to a boxed object array, even if every argument at a particular call site is a primitive.
Under all of this sits the JVM’s 255-parameter ceiling. The Java Virtual Machine Specification (Oracle) caps every method descriptor at 255 parameter slots. Vararg doesn’t change that, since the compiler counts it as one array parameter.
You won’t hit that limit by accident, though. If you do, the function’s design went wrong a long time before the argument count did.
How Does Kotlin Vararg Work With Java?

Well, mostly. Kotlin’s vararg compiles to the same T... form Java has used since J2SE 5.0, so neither side needs adapter code.
The Oracle and OpenJDK issue tracker (JDK-4856541) shows the feature was proposed and shipped in that 5.0 release. It retrofitted existing methods like MessageFormat.format without breaking their existing callers.
If your team is still deciding between Kotlin or Java, small interop details like this usually affect daily comfort more than any headline feature.
Calling a Kotlin Vararg Function From Java
From Java it just looks like a normal T... method in the bytecode. Java callers pass comma-separated arguments like they would to any Java varargs method.
Named arguments are where it breaks down. Java has no syntax for them, and compiled bytecode doesn’t always keep parameter names anyway.
Calling a Java Varargs Method From Kotlin
Going the other way is easy. Kotlin sees a Java String... parameter as a vararg, so MyJava.printStrings("a", "b", "c") works from Kotlin exactly the way it does from Java.
The spread operator only comes in when you already have an array. In that case, write MyJava.printStrings(*arrayOf("a", "b", "c")), or spread whatever array variable you’re holding. If you pass the array without the asterisk, you’ll get the same type mismatch as with a Kotlin function.
Where Does the Kotlin Standard Library Use Vararg?

The collection builders use it, which is why you never write an array literal just to call listOf(1, 2, 3).
Not everything in the standard library does, though. The cases where the Kotlin team avoided vararg tell you something about when it’s worth using.
Collection Builders: listOf, arrayOf, and mapOf
Kotlin’s API reference (kotlinlang.org) documents each of these with its exact signature and version history.
| Function | Signature | Notes |
|---|---|---|
| arrayOf | vararg elements: T | Vararg only, unchanged since Kotlin 1.0 |
| listOf | vararg elements: T | Also has a dedicated single-element overload, available since Kotlin 1.0 |
| mapOf | vararg pairs: Pair<K, V> | Also has a dedicated single-pair overload, available since Kotlin 1.0 |
Why have a single-element overload at all? Calling listOf(x) with one item is really common, and the dedicated version skips building an array entirely. It’s a small optimization for the most frequent call.
println and Other Common Functions
println doesn’t use vararg. It has a separate overload for each primitive type (Int, Long, Byte, Short, Char, Boolean, Float, Double) plus one for Any?.
A vararg println would have boxed every primitive into an Any? just to print it. On every call. The Kotlin team clearly decided that wasn’t worth it.
String.format does take a vararg for its format arguments, the same as the Java method it’s based on.
How Do You Declare and Call a Vararg Function Step by Step?
Whatever the parameter type is, the steps are the same:
- Write the function signature with the vararg keyword directly before the parameter’s type
- Call it with individual comma-separated arguments, which is what most vararg calls look like
- Call it with zero arguments to confirm the parameter is actually optional
- Call it with the spread operator on an existing array, when the values already live in one
- Combine a fixed parameter with a trailing vararg in the same function and try both calling styles
fun logEvent(tag: String, vararg details: String) covers the last two steps together. logEvent("auth", *detailsArray) spreads an array, and logEvent("auth", "login", "success") types the values out.
Both call the same function. The declaration doesn’t care how the caller passes things in.
Honestly, the fastest way to make this stick is to run it. Step through each call in Android Studio‘s debugger and look at how many elements end up in the array each time.
FAQ on What Are Kotlin Varargs
Can You Pass an Existing Array Into a Vararg Parameter Without the Spread Operator?
Not positionally. Without the asterisk, Kotlin treats the array as one argument, and you get a type mismatch.
The one way around it is the named form covered above: mergeStrings(strings = arrayOf("a", "b")).
What Compiler Errors Happen With Incorrect Vararg Usage?
You’ll usually see two. Declaring a second vararg parameter gives you “Multiple vararg-parameters are prohibited.”
Passing an array without spreading it gives a type mismatch, because Kotlin reads the array as a single object.
Can Vararg Be Used With a Lambda Parameter?
Yes, in both ways you might mean. A vararg of a function type is allowed (vararg actions: () -> Unit), and it gets passed like any other vararg.
A vararg followed by a trailing lambda works too. If any other parameter comes after the vararg, though, it needs a named argument.
Is Vararg the Same Concept as \*args in Python?
The idea is the same. Both let a function take a variable number of positional arguments.
The mechanics differ. Python collects \*args into a tuple at runtime with no declared type. Kotlin’s vararg is statically typed and becomes a real array.
Where Do Kotlin Varargs Actually Earn Their Keep?
In small, call-site-driven functions: logging, string formatting, collection builders. The argument count changes from call to call, and pre-building an array would just be noise.
Before reaching for vararg, ask whether the caller already has the values in a collection. If yes, take a List. Same answer if the function runs inside a performance-critical loop, or if you need more than one variable-length group.
Even in the good cases there’s a catch I don’t see mentioned much. A signature that accepts five arguments accepts zero just as happily, so you get no compile-time minimum. If you need at least one, declare a normal first parameter and put the vararg after it.
Constructors have the same one-vararg rule, so Kotlin constructors are a good next read if you want to use this outside plain functions.
- pip is not recognized in VS Code: Why It Happens and How to Fix It - October 1, 2026
- 10 Enterprise Server Virtualization Platforms for Modern Data Centers - October 1, 2026
- MySQL Cheat Sheet - September 30, 2026



