Kotlin系列之命名参数和默认参数

今天一起来看看Kotlin的命名参数和默认参数。

如果你学过Python,那你对这两个概念一定不陌生,那我们今天就来学习一波Kotlin中的默认参数和命名参数。

遇到的问题

为了说明命名参数和默认参数出现的必要性,我们先抛出一个问题,如何打印出一个集合,并且定制它的打印形式。在Java中我们最常用的思路就是重写toString()方法或者是写一个打印集合的工具类,比如下面的Java代码。

Java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {
public static void main(String[] args) {
List<String> mArrayList = new ArrayList<String>(){
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < this.size(); i++) {
stringBuilder.append(this.get(i));
if (i < this.size() - 1){
stringBuilder.append(";");
}
}
return stringBuilder.toString();
}
};
mArrayList.add("123");
mArrayList.add("456");
mArrayList.add("789");
System.out.println(mArrayList);
}
}

上面我们是重写toString()方法,那我们看看在Kotlin中怎么做呢?我们先看看Kotlin中默认的打印样式。

Kotlin代码

1
2
3
4
5
6
fun main(args: Array<String>){
val list = listOf<String>("123", "456", "789")
println(list)
}

//打印结果:[123, 456, 789]

我们现在就先用Kotlin来写一个定制打印集合样式的函数。

Kotlin代码

1
2
3
4
5
6
7
8
9
10
11
12
fun <T> joinToString(collection: Collection<T>, separator: String,
prefix: String, suffix: String): String{
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()){
if (index > 0){
result.append(separator)
}
result.append(element)
}
result.append(suffix)
return result.toString();
}

上面这种方法就是采用自己定义一个工具类,通过传入不同的参数来定制打印的样式。

命名参数

我们这里先来看看如何调用上面的函数,再说说存在的问题。

Kotlin代码

1
println(joinToString(list, ";", "(", ")"))

显然这里有一个很不方便的地方,如果我们不借助于IDE的提示和查看方法定义,我们是很难看懂我们传递给函数的每个参数的具体含义,所以这个时候命名参数出现了。看这个名字就能知道,它是给参数命名,让我们对参数的传入和查看更加清楚,就像下面这样。

Kotlin代码

1
2
3
println(joinToString(list, ";", "(", ")"))
println(joinToString(collection = list, separator = ";", prefix = "(", suffix = ")"))
println(joinToString( separator = ";", collection = list, prefix = "(", suffix = ")"))

看到了吗,我们给传入的每个参数前面加了一个名称,这样是不是很直观呢,而且由于这里的参数是用命名指定的,所以参数的顺序可以和定义函数的顺序不同,上面的三种方式都是可以正确调用的。
但是这里有一些注意事项:

  1. 如果我们已经指定了一个参数的名称,那么这个参数其后的参数也必须指定名称,不让就会造成混淆,Kotlin编译器就不能明白你的意图。
  2. 命名参数的形式只能用于Kotlin的函数,不能用于Java的函数,就像下面这样是错误的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //Java代码
    public class Main {
    public static void javaFun(String name){
    System.out.println(name);
    }
    }

    //Kotlin代码
    fun main(args: Array<String>){
    //会报错[命名参数不能用于Java函数]
    println(Main.javaFun(name = "bingjianit"))
    }

默认参数

我们再回到文章开头的那个需求,我们希望我们使用Kotlin写的函数,可以在我们不传入分隔符是,默认使用”,”,在我们传入分隔符时使用我们传入的分隔符,也就是让我们的函数的某些参数具有默认值。其实同样的需求我们在Java中可以通过重载函数来实现,但是那会造成会出现很多个重载函数,在Kotlin中,我们可以通过默认参数来实现这个需求。

Kotlin代码

1
2
3
4
5
6
7
8
9
10
11
12
fun <T> joinToString2(collection: Collection<T>, separator: String = ",",
prefix: String = "[", suffix: String = "]"): String{
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()){
if (index > 0){
result.append(separator)
}
result.append(element)
}
result.append(suffix)
return result.toString();
}

上面的代码中我们为后面三个参数指定了默认值,当我们不传入值时,使用默认值,传入值时使用我们自己的参数,我们可以像下面一样调用。

Kotlin代码

1
2
3
4
//输出 [123,456,789]
println(joinToString2(list))
//输出 [123;456;789]
println(joinToString2(list, separator = ";"))

可以看出,我们这里其实是结合使用了默认参数和命名参数。

由于Kotlin与Java可以互操作,那么上面的函数在Java中怎么调用呢?由于Java中不支持默认参数,所以我们必须为函数传入每一个参数。就像下面这样:
我们在Str.kt文件中写了我们上面定义的有默认值的函数

1
2
//在Java中调用[必须传入全部的参数]
System.out.println(StrKt.joinToString2(mArrayList, ",", "{", "}"));

当然,如果你不想写这么繁琐,可以在Kotlin默认函数生成时添加一个@JvmOverloads注解,编译器就会自动为每个默认参数生成重载函数,就像下面这样

Kotlin代码

1
2
3
4
5
6
7
8
9
10
11
12
13
@JvmOverloads
fun <T> joinToString2(collection: Collection<T>, separator: String = ",",
prefix: String = "[", suffix: String = "]"): String{
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()){
if (index > 0){
result.append(separator)
}
result.append(element)
}
result.append(suffix)
return result.toString();
}

生成的Java重载函数如下
Java代码

1
2
3
4
public static <T> T joinToString2(Collection<T> collection);
public static <T> T joinToString2(Collection<T> collection, String separator);
public static <T> T joinToString2(Collection<T> collection, String separator, String prefix);
public static <T> T joinToString2(Collection<T> collection, String separator, String prefix, String suffix);

这样我们在调用时就不需要传那么多参数了。

写在最后

Kotlin结合了多种语言的优秀特性,变成了一门简洁、精致的编程语言,并且还具备了和Java无缝的互操作性。

如果博客对您有帮助,不妨请我喝杯咖啡...