Kotlin中可变参数

对比 Java 中的可变参数

先看下 Java 的可变参数,用我们最熟悉的 main 函数

public static void main(String... args) {
}

很多人可能都觉得不对啊,参数应该是数组啊

public static void main(String[] args) {
}

其实上面两种写法是一样的,从 Java5 开始引入了可变参数(varargs)

对应的 Kotlin 的代码,也是两种方式,参数为数组:

fun main(args: Array<String>) {
}

参数为可变参数:

fun main(vararg args: String) {
}

可变参数的本质

和 Java 一样,可变参数的本质就是数组,我们来测试一下:

fun main() {
    foo("1", "2", "3")
}

fun foo(vararg args: String) {
    println(args::class)
    println(args.contentToString())
    for (i in args.indices) {
        println(args[i])
    }
}

打印:

class kotlin.Array
val kotlin.reflect.KClass<T>.java: java.lang.Class<T>
[1, 2, 3]
1
2
3

我们可以清晰的看到 args 的类型为数组类型,并且可以直接调用数组的方法。

如果你的第一行打印结果是:
class [Ljava.lang.String; (Kotlin reflection is not available)
需要在 build.gradle 中添加依赖:
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

准确的说 args 的类型是 Array<out String> 类型:

fun main() {
    foo("1", "2", "3")
}

fun foo(vararg args: String) {
    bar(args)
}

fun bar(args: Array<out String>) {
    println(args.contentToString())
}

可变参数的传参

我们再来改造一下 bar 函数的参数类型,看下可变参数的传参:

fun main() {
    foo("1", "2", "3")
}

fun foo(vararg args: String) {
    bar(args)
}

fun bar(vararg args: String) {
    println(args.contentToString())
}

编译器提示类型不匹配,需要一个 String 类型的参数,而传入了数组类型。

传参报错

在 Java 中可变参数是可以直接传递,并且可以和数组相互转换传递:

public static void main(String[] args) {
    foo("1", "2", "3");
}

private static void foo(String... args) {
    bar1(args);
    bar2(args);
}

private static void bar1(String... args) {
    System.out.println(Arrays.toString(args));
}

private static void bar2(String[] args) {
    bar1(args);
}

在 Kotlin 中如果想将数组类型传入到可变参数,就需要使用一个特定的符号 *

fun main() {
    foo("1", "2", "3")
}

fun foo(vararg args: String) {
    bar1(*args)
    bar2(args)
}

fun bar1(vararg args: String) {
    println(args.contentToString())
}

fun bar2(args: Array<out String>) {
    bar1(*args)
}

总结

我对 Kotlin 中d可变参数的理解是:

  1. 可变参数会在函数体中,自动转变为数组类型
  2. 数组类型不能作为参数,直接传递给可变参数
  3. 在数组类型前面添加 * ,可以传递给可变参数

最后,我们可以反编译看下 Kotlin 中的 foo 函数,看看 * 到底做了什么:

public static final void foo(@NotNull String... args) {
    bar1((String[]) Arrays.copyOf(args, args.length));
    bar2(args);
}

就是一个复制数组的操作,相比 Kotlin 还是 Java 做的更便捷,可以在数组和可变参数之间直接自由转换。

推荐阅读更多精彩内容