Kotlin Variable Arguments (varargs)
Introduction
When creating functions in Kotlin, there are situations where you might want to pass a varying number of arguments of the same type. Instead of defining multiple overloaded functions or requiring the caller to create an array or list first, Kotlin provides a neat feature called variable arguments (or varargs for short).
Variable arguments allow you to define a function that can accept any number of arguments (including zero) of a particular type. This feature makes your code more flexible and often more readable when working with multiple values of the same type.
Understanding Variable Arguments
Basic Syntax
In Kotlin, you can define a parameter as a vararg by adding the vararg
modifier before the parameter name:
fun functionName(vararg paramName: Type) {
// Function body
}
Inside the function, the parameter is treated as an array of the specified type.
A Simple Example
Let's start with a basic example that calculates the sum of any number of integers:
fun sum(vararg numbers: Int): Int {
var result = 0
for (number in numbers) {
result += number
}
return result
}
// Usage
fun main() {
println(sum(1, 2, 3)) // Output: 6
println(sum(10, 20)) // Output: 30
println(sum()) // Output: 0 (called with zero arguments)
println(sum(5, 10, 15, 20)) // Output: 50
}
In this example, numbers
is treated as an array of Int
values within the function. We can call the function with any number of integers, and they'll all be accessible through the numbers
parameter.
Key Features of Varargs
Varargs and Regular Parameters
You can combine vararg parameters with regular parameters. The vararg parameter is usually placed last:
fun greetPeople(greeting: String, vararg names: String) {
for (name in names) {
println("$greeting, $name!")
}
}
fun main() {
greetPeople("Hello", "Alice", "Bob", "Charlie")
}
Output:
Hello, Alice!
Hello, Bob!
Hello, Charlie!
Only One Vararg Parameter
A function can have only one vararg parameter. The following would cause a compilation error:
// This will NOT compile
fun invalidFunction(vararg numbers: Int, vararg names: String) {
// Function body
}
Varargs in the Middle
If you need to have a parameter after the vararg parameter, you must use named arguments when calling the function:
fun formatMessage(prefix: String, vararg items: String, suffix: String): String {
return "$prefix ${items.joinToString(", ")} $suffix"
}
fun main() {
// Must use named parameter for suffix
val message = formatMessage("Items:", "Apple", "Banana", "Cherry", suffix: "are fruits")
println(message)
}
Output:
Items: Apple, Banana, Cherry are fruits
Spreading Arrays into Varargs
If you already have an array and want to pass its contents as varargs, you can use the spread operator (*):
fun main() {
val numbers = intArrayOf(1, 2, 3, 4, 5)
// Using the spread operator (*)
println(sum(*numbers)) // Output: 15
// You can also combine spread arrays with regular arguments
println(sum(10, *numbers, 20)) // Output: 45 (10 + 1 + 2 + 3 + 4 + 5 + 20)
}
This is particularly useful when you need to combine existing arrays with other values.
Practical Examples
Example 1: Creating a Logger Function
fun log(level: String, vararg messages: Any) {
val timestamp = java.time.LocalDateTime.now().toString()
val combinedMessage = messages.joinToString(" ")
println("[$timestamp] $level: $combinedMessage")
}
fun main() {
log("INFO", "User", "John", "logged in")
log("ERROR", "Database connection failed", "Retry count:", 3)
}
Output:
[2023-10-25T15:30:45.123] INFO: User John logged in
[2023-10-25T15:30:45.125] ERROR: Database connection failed Retry count: 3
Example 2: Building a Menu Creator
data class MenuItem(val name: String, val price: Double)
fun createMenu(restaurantName: String, vararg items: MenuItem): String {
val menuHeader = "=== $restaurantName Menu ===\n"
val menuItems = items.mapIndexed { index, item ->
"${index + 1}. ${item.name} - $${item.price}"
}.joinToString("\n")
return menuHeader + menuItems
}
fun main() {
val menu = createMenu("Kotlin Cafe",
MenuItem("Coffee", 2.50),
MenuItem("Sandwich", 5.95),
MenuItem("Salad", 4.75),
MenuItem("Cake", 3.25)
)
println(menu)
}
Output:
=== Kotlin Cafe Menu ===
1. Coffee - $2.5
2. Sandwich - $5.95
3. Salad - $4.75
4. Cake - $3.25
Example 3: Function to Find Maximum Value
fun findMax(vararg numbers: Int): Int {
if (numbers.isEmpty()) throw IllegalArgumentException("Cannot find maximum of empty list")
var max = numbers[0]
for (number in numbers) {
if (number > max) max = number
}
return max
}
fun main() {
println(findMax(1, 5, 3, 9, 2)) // Output: 9
val scores = intArrayOf(85, 92, 78, 95, 88)
println(findMax(*scores)) // Output: 95
}
Best Practices
-
Use varargs sparingly: While convenient, overusing varargs can make your API less clear. Use them when it genuinely makes sense for the function to accept a variable number of arguments.
-
Place vararg parameters last: This makes your function calls cleaner and avoids the need for named parameters.
-
Consider alternatives: For complex cases, sometimes explicitly passing a collection (List, Set, etc.) might be more appropriate than using varargs.
-
Be careful with performance: Remember that varargs create a new array each time the function is called, which can impact performance in critical code.
Common Mistakes to Avoid
Confusion with Collections
Varargs and collections are different:
// This takes a vararg of integers
fun processNumbers(vararg numbers: Int) { /* ... */ }
// This takes a single List parameter
fun processNumbersList(numbers: List<Int>) { /* ... */ }
fun main() {
val list = listOf(1, 2, 3)
// Wrong: This passes the List as a single argument
processNumbers(list) // ERROR - Type mismatch
// Correct: Spread the list's contents
processNumbers(*list.toIntArray())
// Correct: Pass the list as intended
processNumbersList(list)
}
Forgetting the Spread Operator
When you have an array that you want to pass to a vararg parameter, forgetting the spread operator will pass the array as a single argument:
fun main() {
val numbers = intArrayOf(1, 2, 3)
// Wrong: This passes the array as a single argument
// sum(numbers) // ERROR - Type mismatch
// Correct: This spreads the array into individual arguments
sum(*numbers)
}
Summary
Kotlin's variable arguments (varargs) provide a flexible way to define functions that can accept a varying number of arguments. Key points to remember:
- Use the
vararg
modifier to define a parameter that can accept any number of arguments - Inside the function, the vararg parameter is treated as an array
- A function can only have one vararg parameter
- Use the spread operator (
*
) to pass the contents of an array as varargs - Vararg parameters work well with regular parameters but are typically defined last
Variable arguments make your code more expressive and can eliminate the need for overloaded functions in many cases. They're particularly useful for functions like logging, data processing, and utility functions where the number of inputs can vary.
Exercises
-
Create a function called
concatenate
that takes a vararg of strings and joins them together with a specified separator. -
Implement a function called
filterPositive
that accepts vararg integers and returns only the positive numbers as a list. -
Write a
printTable
function that takes a vararg of any objects and prints them as a formatted table row. -
Create a function that calculates the average of a variable number of double values.
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)