可变参数列表
vararg关键字生成一个具有灵活大小的参数列表。
在 列表 中,我们介绍了 listOf(),它接受任意数量的参数并生成一个 List:
// Varargs/ListOf.kt
import atomictest.eq
fun main() {
listOf(1) eq "[1]"
listOf("a", "b") eq "[a, b]"
}
使用 vararg 关键字,您可以定义一个函数,该函数接受任意数量的参数,就像 listOf() 一样。vararg 是 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)
}
函数定义可以只指定一个参数为 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
}
尽管 Array 和 List 看起来相似,但它们的实现方式不同 —— 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]
}
如果您传递一个原始类型(如 Int、Double 或 Boolean)的 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 提供了转换函数,例如用于转换为 Int 的 toInt(),以及用于转换为 Float 的 toFloat()。使用这些函数时,假定命令行参数按照特定顺序出现。在这里,程序期望一个 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() 中的第一行在没有足够的参数时退出程序。如果您未提供可转换为 Int 和 Float 的内容作为第二个和第三个命令行参数,您将看到运行时错误(可以尝试以查看错误)。
使用与之前相同的命令行参数编译并运行 MainArgConversion.kt,您将看到:
hamster 42 3.14159
练习和解答可以在 www.AtomicKotlin.com 找到。