Java中的变长参数

先来看看Java中的变长参数,很简单,为了后面的对比,我们做进行一个连续的传参:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Arrays;

public class TestVarargs {
public static void main(String[] args) {
test1("1", "2");
}

private static void test1(String... args) {
test2(args);
}

private static void test2(Object... args) {
System.out.println(Arrays.toString(args));
System.out.println(args.length);
}
}
// 输出结果
// [1, 2]
// 2

我们都知道变长参数在Java中实际上是一个数组,所以可以用 Arrays.toString 方法。

Kotlin中变长参数的小坑

但如果把上面代码换成Kotlin呢?(在Kotlin中用 vararg 关键字表示变长参数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main(args: Array<String>) {
test1("1", "2")
}

private fun test1(vararg args: String) {
test2(args)
}

private fun test2(vararg args: Any) {
println(args.contentToString())
println("${args.size}")
}
// 输出结果
// [[Ljava.lang.String;@65ab7765]
// 1

什么鬼?明明是2个参数,为什么长度是1呢?定睛一看,args 数组里竟然只有一个元素,而且类型是 String[]。原来在test1方法中它把args数组当成一个整体传入了test2,所以test2中的args数组的第一个元素就是一个String数组。

如何解决呢?实际上只要在test1方法中给传参加上 * 就可以了,这个星号在Kotlin变长参数中叫Spread操作,可以理解为“打散”、“分散”。代码如下:

1
2
3
4
5
6
private fun test1(vararg args: String) {
test2(*args)
}
// 最终输出结果
// [1, 2]
// 2

这样就和Java中期望的结果一致了。

其实这个问题最关键的并不是忘了加星号,而是方法的参数类型给你设了个陷阱,注意test2方法的参数类型,是Any(等同于Java中的Object),所以在test1传参时,test2可以接受任何类型的参数,因此才把args数组当作了一个整体,而不会把它像Java一样自动分散成变长参数:

在这里插入图片描述

如果我们把test2的方法参数改成String,IDE就会自动提示你需要加星号了:

在这里插入图片描述

这也是初学Kotlin时比较容易犯的错误。