今天一起来看看Kotlin的命名参数和默认参数。
如果你学过Python,那你对这两个概念一定不陌生,那我们今天就来学习一波Kotlin中的默认参数和命名参数。
遇到的问题
为了说明命名参数和默认参数出现的必要性,我们先抛出一个问题,如何打印出一个集合,并且定制它的打印形式。在Java中我们最常用的思路就是重写toString()方法或者是写一个打印集合的工具类,比如下面的Java代码。
Java代码
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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
上面我们是重写toString()方法,那我们看看在Kotlin中怎么做呢?我们先看看Kotlin中默认的打印样式。
Kotlin代码
fun main(args: Array<String>){
val list = listOf<String>("123", "456", "789")
println(list)
}
//打印结果:[123, 456, 789]
- 1
- 2
- 3
- 4
- 5
- 6
我们现在就先用Kotlin来写一个定制打印集合样式的函数。
Kotlin代码
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();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
上面这种方法就是采用自己定义一个工具类,通过传入不同的参数来定制打印的样式。
命名参数
我们这里先来看看如何调用上面的函数,再说说存在的问题。
Kotlin代码
println(joinToString(list, ";", "(", ")"))
- 1
显然这里有一个很不方便的地方,如果我们不借助于IDE的提示和查看方法定义,我们是很难看懂我们传递给函数的每个参数的具体含义,所以这个时候命名参数
出现了。看这个名字就能知道,它是给参数命名,让我们对参数的传入和查看更加清楚,就像下面这样。
Kotlin代码
println(joinToString(list, ";", "(", ")"))
println(joinToString(collection = list, separator = ";", prefix = "(", suffix = ")"))
println(joinToString( separator = ";", collection = list, prefix = "(", suffix = ")"))
- 1
- 2
- 3
看到了吗,我们给传入的每个参数前面加了一个名称,这样是不是很直观呢,而且由于这里的参数是用命名指定的,所以参数的顺序可以和定义函数的顺序不同,上面的三种方式都是可以正确调用的。
但是这里有一些注意事项:
1. 如果我们已经指定了一个参数的名称,那么这个参数其后的参数也必须指定名称,不让就会造成混淆,Kotlin编译器就不能明白你的意图。
2. 命名参数的形式只能用于Kotlin的函数,不能用于Java的函数,就像下面这样是错误的。
//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"))
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
默认参数
我们再回到文章开头的那个需求,我们希望我们使用Kotlin写的函数,可以在我们不传入分隔符是,默认使用”,”,在我们传入分隔符时使用我们传入的分隔符,也就是让我们的函数的某些参数具有默认值。其实同样的需求我们在Java中可以通过重载函数来实现,但是那会造成会出现很多个重载函数,在Kotlin中,我们可以通过默认参数来实现这个需求。
Kotlin代码
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();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
上面的代码中我们为后面三个参数指定了默认值,当我们不传入值时,使用默认值,传入值时使用我们自己的参数,我们可以像下面一样调用。
Kotlin代码
//输出 [123,456,789]
println(joinToString2(list))
//输出 [123;456;789]
println(joinToString2(list, separator = ";"))
- 1
- 2
- 3
- 4
可以看出,我们这里其实是结合使用了默认参数和命名参数。
由于Kotlin与Java可以互操作,那么上面的函数在Java中怎么调用呢?由于Java中不支持默认参数,所以我们必须为函数传入每一个参数。就像下面这样:
我们在Str.kt
文件中写了我们上面定义的有默认值的函数
//在Java中调用[必须传入全部的参数]
System.out.println(StrKt.joinToString2(mArrayList, ",", "{", "}"));
- 1
- 2
当然,如果你不想写这么繁琐,可以在Kotlin默认函数生成时添加一个@JvmOverloads注解,编译器就会自动为每个默认参数生成重载函数,就像下面这样
Kotlin代码
@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();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
生成的Java重载函数如下
Java代码
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);
- 1
- 2
- 3
- 4
这样我们在调用时就不需要传那么多参数了。
写在最后
Kotlin结合了多种语言的优秀特性,变成了一门简洁、精致的编程语言,并且还具备了和Java无缝的互操作性。