Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

可变参数列表

vararg 关键字生成一个具有灵活大小的参数列表。

列表 中,我们介绍了 listOf(),它接受任意数量的参数并生成一个 List

// Varargs/ListOf.kt
import atomictest.eq

fun main() {
  listOf(1) eq "[1]"
  listOf("a", "b") eq "[a, b]"
}

使用 vararg 关键字,您可以定义一个函数,该函数接受任意数量的参数,就像 listOf() 一样。varargvariable 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)
}

函数定义可以只指定一个参数为 vararg。虽然在参数列表中的任何项目都可以指定为 vararg,但通常最简单的做法是对最后一个参数进行这样的操作。

vararg 允许您传递任意数量(包括零个)的参数。所有参数都必须是指定类型的。使用参数名称访问 vararg 参数,参数名称将成为一个 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
}

尽管 ArrayList 看起来相似,但它们的实现方式不同 —— List 是一个常规库类,而 Array 具有特殊的低级支持。Array 来自 Kotlin 与其他语言(尤其是 Java)兼容的要求。

在日常编程中,当您需要一个简单的序列时,请使用 List。仅在第三方 API 需要一个 Array 或者当您处理 vararg 时才使用 Array

在大多数情况下,您可以忽略 vararg 生成 Array 的事实,并将其视为 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
  """
}

您可以在接受 vararg 的地方传递元素的 Array。要创建 Array,请使用与使用 listOf() 相同的方式使用 arrayOf()。注意,Array 总是可变的。要将 Array 转换为参数序列(不仅仅是类型为 Array 的单个元素),请使用 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]
  // 不编译:
  // sum(1, 2, 3, array, 6)

  val list = listOf(9, 10, 11)
  sum(*list.toIntArray()) eq 30  // [2]
}

如果您传递一个原始类型(如 IntDoubleBoolean)的 Array,则 Array 创建函数必须具有特定的类型。如果在 [1] 行使用 arrayOf(4, 5) 而不是 intArrayOf(4, 5),将会产生一个错误,提示 inferred type is Array<Int> but IntArray was expected

扩展操作符仅适用于数组。如果您有一个要作为参数序列传递的 List,请首先将其转换为 Array,然后应用扩展操作符,如 [2]。由于结果是原始类型的 Array,我们必须再次使用特定的转换函数 toIntArray()

当您必须将 vararg 参数传递给另一个函数,而该函数也期望 vararg 参数时,扩展操作符尤其有用:

// 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]"
}

命令行参数

在命令行上调用程序时,您可以向其传递可变数量的参数。要捕获命令行参数,必须为 main() 提供一个特定的参数:

// Varargs/MainArgs.kt

fun main(args: Array<String>) {
  for (a in args) {
    println(a)
  }
}

参数传统上称为 args(尽管您可以称其为任何名称),而 args 的类型只能是 Array<String>String 的数组)。

如果您使用 IntelliJ IDEA,您可以通过编辑相应的“运行配置”来传递程序参数,就像本课程的最后一个练习中所示。

您还可以使用 kotlinc 编译器来生成命令行程序。如果您的计算机上没有安装 kotlinc,请按照 Kotlin 官方网站 上的说明操作。一旦您输入并保存了 MainArgs.kt 的代码,可以在命令提示符中键入以下内容:

kotlinc MainArgs.kt

在程序调用后,您可以提供命令行参数,如下所示:

kotlin MainArgsKt hamster 42 3.14159

您将看到以下输出:

hamster
42
3.14159

如果您希望将 String 参数转换为特定类型,Kotlin 提供了转换函数,例如用于转换为 InttoInt(),以及用于转换为 FloattoFloat()。使用这些函数时,假定命令行参数按照特定顺序出现。在这里,程序期望一个 String,后跟可转换为 Int 的内容,后跟可转换为 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")
}

main() 中的第一行在没有足够的参数时退出程序。如果您未提供可转换为 IntFloat 的内容作为第二个和第三个命令行参数,您将看到运行时错误(可以尝试以查看错误)。

使用与之前相同的命令行参数编译并运行 MainArgConversion.kt,您将看到:

hamster  42  3.14159

练习和解答可以在 www.AtomicKotlin.com 找到。