1. 概述
在本篇简明教程中,我们将学习如何在 Kotlin 中使用可变参数(varargs)向函数传递不定数量的参数。同时也会介绍如何将数组转换为 varargs 参数。
最后我们会从字节码层面看 Kotlin 是如何实现 varargs 的。
2. Varargs 的基本使用
如果我们希望一个函数能接受任意数量的参数,可以在参数前加上 vararg
关键字:
fun sum(vararg xs: Int): Int = xs.sum()
✅ 上述函数可以接受 0 个或多个 Int
类型参数。例如:
val zeroNumbers = sum()
assertEquals(0, zeroNumbers)
assertEquals(2, sum(2))
assertEquals(12, sum(2, 4, 6))
⚠️ 函数体内,vararg
参数会被当作数组来处理。例如下面的例子:
fun <T> printAll(vararg ts: T) {
ts.forEach { println(it) }
}
对于引用类型,vararg
会被编译为对应的 Array<T>
;对于基本类型(如 Int
),则会被编译为专用数组类型如 IntArray
。
3. Varargs 的限制
每个函数最多只能有一个
vararg
参数。否则编译器会报错:Kotlin: Multiple vararg-parameters are prohibited
虽然 Kotlin 允许将
vararg
参数放在任意位置(不同于 Java),但为了调用清晰,建议尽量放在最后:fun createUser(vararg roles: String, username: String, age: Int) { // ... }
如果
vararg
不在最后,调用时必须使用命名参数来避免歧义:createUser("admin", "user", username = "me", age = 42)
❌ 否则会编译失败。
4. Spread 操作符的使用
当我们已有数组,想将它作为 vararg
参数传入函数时,需要使用 spread 操作符 *
来“展开”数组:
val numbers = intArrayOf(1, 2, 3, 4)
val summation = sum(*numbers)
assertEquals(10, summation)
✅ *numbers
就是 spread 操作符的使用方式。
5. Kotlin Varargs 的字节码实现
在底层,Kotlin 会将 vararg
参数翻译为 Java 的 varargs
:
例如我们编译如下 Kotlin 代码:
$ kotlinc Vararg.kt
然后用 javap
查看字节码:
$ javap -c -p -v com.baeldung.varargs.VarargKt
可以看到:
public static final int sum(int...);
descriptor: ([I)I
flags: (0x0099) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_VARARGS
说明 Kotlin 的 vararg
最终会被编译为 Java 的 vararg
,并带有 ACC_VARARGS
标记。
Spread 操作符的字节码处理
当我们使用 spread 操作符时,Kotlin 实际上是通过调用 Arrays.copyOf()
创建数组副本,再传给函数:
val numbers = intArrayOf(1, 2)
sum(*numbers)
对应字节码中会调用:
12: aload_0
13: dup
14: arraylength
15: invokestatic #71 // Method java/util/Arrays.copyOf:([II)[I
18: invokestatic #72 // Method sum:([I)I
非末尾 vararg 的处理
如果我们在 Kotlin 中定义非末尾的 vararg
参数:
fun createUser(vararg roles: String, username: String, age: Int) {
// ...
}
Kotlin 编译器会将其翻译为普通的数组参数:
public static final void createUser(java.lang.String[], java.lang.String, int);
⚠️ 这是因为 Java 的 vararg
必须是最后一个参数,Kotlin 为兼容性做了适配处理。
6. 总结
- Kotlin 使用
vararg
关键字实现可变参数。 - 函数体内,
vararg
会被当作数组处理。 - 每个函数只能有一个
vararg
,但可以放在任意位置(需注意命名参数调用)。 - 使用 spread 操作符
*
可将数组展开为vararg
。 - Kotlin 在底层将
vararg
翻译为 Java 的vararg
或数组,具体取决于参数位置。 - 使用 spread 时,Kotlin 会复制数组并传入副本。
完整示例代码请参考 GitHub 项目。