Variable Argument Lists

The vararg keyword 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.