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 项目


原始标题:Varargs and Spread Operator in Kotlin