Variable Argument Lists
The
varargkeyword produces a flexibly-sized argument list.
In Lists we introduced listOf(), which takes any number of parameters and
produces a List:
// Varargs/ListOf.kt
import atomictest.eq
fun main() {
listOf(1) eq "[1]"
listOf("a", "b") eq "[a, b]"
}
Using the vararg keyword, you can define a function that takes any number of
arguments, just like listOf() does. vararg is short for variable argument
list:
// Varargs/VariableArgList.kt
package varargs
fun v(s: String, vararg d: Double) {}
fun main() {
v("abc", 1.0, 2.0)
v("def", 1.0, 2.0, 3.0, 4.0)
v("ghi", 1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
}
A function definition may specify only one parameter as vararg. Although it’s
possible to specify any item in the parameter list as vararg, it’s usually
simplest to do it for the last one.
vararg allows you to pass any number (including zero) of arguments. All
arguments must be of the specified type. vararg arguments are accessed using
the parameter name, which becomes an Array:
// Varargs/VarargSum.kt
package varargs
import atomictest.eq
fun sum(vararg numbers: Int): Int {
var total = 0
for (n in numbers) {
total += n
}
return total
}
fun main() {
sum(13, 27, 44) eq 84
sum(1, 3, 5, 7, 9, 11) eq 36
sum() eq 0
}
Although Arrays and Lists look similar, they are implemented
differently—List is a regular library class while Array has special
low-level support. Array comes from Kotlin’s requirement for compatibility
with other languages, especially Java.
In day-to-day programming, use a List when you need a simple sequence. Use
Arrays only when a third-party API requires an Array, or when you’re
dealing with varargs.
In most cases you can just ignore the fact that vararg produces an Array
and treat it as if it were a List:
// Varargs/VarargLikeList.kt
package varargs
import atomictest.eq
fun evaluate(vararg ints: Int) =
"Size: ${ints.size}\n" +
"Sum: ${ints.sum()}\n" +
"Average: ${ints.average()}"
fun main() {
evaluate(10, -3, 8, 1, 9) eq """
Size: 5
Sum: 25
Average: 5.0
"""
}
You can pass an Array of elements wherever a vararg is accepted. To create
an Array, use arrayOf() in the same way you use listOf(). An
Array is always mutable. To convert an Array into a sequence of arguments
(not just a single element of type Array), use the spread operator, *:
// Varargs/SpreadOperator.kt
import varargs.sum
import atomictest.eq
fun main() {
val array = intArrayOf(4, 5)
sum(1, 2, 3, *array, 6) eq 21 // [1]
// Doesn't compile:
// sum(1, 2, 3, array, 6)
val list = listOf(9, 10, 11)
sum(*list.toIntArray()) eq 30 // [2]
}
If you pass an Array of primitive types (like Int, Double or Boolean)
as in the example above, the Array creation function must be specifically
typed. If you use arrayOf(4, 5) instead of intArrayOf(4, 5), line [1]
will produce an error complaining that inferred type is Array<Int> but
IntArray was expected.
The spread operator only works with arrays. If you have a List that you want
to pass as a sequence of arguments, first convert it to an Array and then
apply the spread operator, as in [2]. Because the result is an Array of
a primitive type, we must again use the specific conversion function
toIntArray().
The spread operator is especially helpful when you must pass vararg arguments
to another function that also expects varargs:
// Varargs/TwoFunctionsWithVarargs.kt
package varargs
import atomictest.eq
fun first(vararg numbers: Int): String {
var result = ""
for (i in numbers) {
result += "[$i]"
}
return result
}
fun second(vararg numbers: Int) =
first(*numbers)
fun main() {
second(7, 9, 32) eq "[7][9][32]"
}
Command-Line Arguments
When invoking a program on the command line, you can pass it a variable number
of arguments. To capture command-line arguments, you must provide a particular
parameter to main():
// Varargs/MainArgs.kt
fun main(args: Array<String>) {
for (a in args) {
println(a)
}
}
The parameter is traditionally called args (although you can call it
anything), and the type for args can only be Array<String> (Array of
String).
If you are using IntelliJ IDEA, you can pass program arguments by editing the corresponding “Run configuration,” as shown in the last exercise for this atom.
You can also use the kotlinc compiler to produce a command-line program. If
kotlinc isn’t on your computer, follow the instructions on the Kotlin main
site. Once you’ve entered and saved the code for
MainArgs.kt, type the following at a command prompt:
kotlinc MainArgs.kt
You provide the command-line arguments following the program invocation, like this:
kotlin MainArgsKt hamster 42 3.14159
You’ll see this output:
hamster
42
3.14159
If you want to turn a String parameter into a specific type, Kotlin provides
conversion functions, such as a toInt() for converting to an Int, and
toFloat() for converting to a Float. Using these assumes that the
command-line arguments appear in a particular order. Here, the program expects
a String, followed by something convertible to an Int, followed by
something convertible to a Float:
// Varargs/MainArgConversion.kt
fun main(args: Array<String>) {
if (args.size < 3) return
val first = args[0]
val second = args[1].toInt()
val third = args[2].toFloat()
println("$first $second $third")
}
The first line in main() quits the program if there aren’t enough arguments.
If you don’t provide something convertible to an Int and a Float as the
second and third command-line arguments, you will see runtime errors (try it to
see the errors).
Compile and run MainArgConversion.kt with the same command-line arguments we
used before, and you’ll see:
hamster 42 3.14159
Exercises and solutions can be found at www.AtomicKotlin.com.