标签: kotlin 消除函数重载
目录:
一、函数重载的带来问题
1、遇到问题
无论是在Java或者C++中都有函数重载一说,函数重载目的为了针对不同功能业务需求,然后暴露不同参数的接口,包括参数列表个数,参数类型,参数顺序。但是这些参数不同的调整无疑会增加更多同名的函数,对于外部调用者特别的confused. 很容易出现调用错误,特别当存在参数个数相同,只是参数类型不同,就很容易出错。
请看以下例子(以Thread类为例):
public class Thread{ public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } public Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0); } public Thread(ThreadGroup group, Runnable target, String name) { init(group, target, name, 0); } }
class ThreadTest{ public static void main(String[] args){ Thread thread1 = new Thread(); Thread thread2 = new Thread(new Runnable() { @Override public void run() {//todo } }); Thread thread3 = new Thread(null, new Runnable() { @Override public void run() {//todo } }); Thread thread4 = new Thread(null, new Runnable() { @Override public void run() {//todo } }, "thread_test") } }
2、解决问题
由以上的问题,我们知道在Java中会出现很多重载方法,如果调用错误了,也许我们会把这个问题归结于自身不够严谨,可谁曾想过如果一门好的语言是能够在语法上就很明确并且在语法上减少一些错误。然而Kotlin这门语言就在方面优越于Java,它语法的层面上就降低了代码犯错可能。
解决以上问题就是利用Kotlin中的函数命名参数和默认值参数就能彻底消除函数重载的问题。Kotlin解决例子:
class Thread(group: ThreadGroup? = null,
target: Runnable? = null,
name: String? = null)
: Runnable{
init(group, target, name, 0)
}
fun main(agrs: Array<String>){
val thread1= Thread()
val thread2 = Thread(target = object: Runnable{
override fun run() {
//todo
}
})
val thread3 = Thread(target = object: Runnable{
override fun run() {
//todo
}
}, name = "therad_test")
}
二、Kotlin中的函数命名参数
我们需要关注的第一个问题就是函数的可读性,然而函数命名参数的出现就是为了解决函数的可读性,准确来说是为了让调用者更加具有可读性。
定义
在Kotlin中,当调用一个Kotlin定义的函数时,可以显示地标明一些参数的名称,而且可以打乱顺序参数调用顺序,因为可以通过参数名称就能唯一定位具体对应参数。
扫描二维码关注公众号,回复: 121386 查看本文章Java中调用的例子
//定义一个Java函数 public static String joinToString(Collection<T> collection, String prefix, String seperataor, String postfix){ //... } //调用 joinToString(collection, " ", " "," ")
由以上Java中调用joinToString方法其实就比较confused,特别是后面三个空格但从调用的地方根本就无法知道joinToString方法对应的参数是哪个,每个值具体意思是什么,那么就值得你去尝试一下kotlin了。
Kotlin中调用的例子
//函数使用默认值参数 可以消除重载 @JvmOverloads fun <T> joinString( collection: Collection<T>, separator: String, prefix: String, postfix: String ): String { return collection.joinToString(separator, prefix, postfix) } //调用 fun main(args: Array<String>) { println(joinString(collection = listOf(1, 2, 3, 4), separator = "%", prefix = "<", postfix = ">")) }
实际上单单靠命名参数是不能解决函数重载的,命名函数只是纯粹地为了函数调用更加明确和调用者传入的值可以与声明函数的参数对应。要想解决函数重载问题得靠默认值参数来解决
三、Kotlin中的函数默认值参数
当我们在处理默认值参数的时候,命名参数就十分有用,并且一般都使用两者结合,然后解决函数重载的问题。在Kotlin中,可以在声明函数的时候,指定参数的默认值,就可以避免函数的重载。如果再结合使用命名参数,可以省略中间的一些参数,也可以以你想要的任意顺序只给定你需要的参数。
//函数使用默认值参数 可以消除重载
@JvmOverloads
fun <T> joinString(
collection: Collection<T> = listOf(),
separator: String = ",",
prefix: String = "",
postfix: String = ""
): String {
return collection.joinToString(separator, prefix, postfix)
}
//调用
fun main(args: Array<String>) {
//函数使用命名参数可以提高代码可读性
println(joinString(collection = listOf(1, 2, 3, 4), separator = "%", prefix = "<", postfix = ">"))
println(joinString(collection = listOf(1, 2, 3, 4), prefix = "<", postfix = ">"))
println(joinString(collection = listOf(1, 2, 3, 4), separator = "!", prefix = "<"))
println(joinString(collection = listOf(1, 2, 3, 4), separator = "!", postfix = ">"))
println(joinString(collection = listOf(1, 2, 3, 4), separator = "!"))
println(joinString(collection = listOf(1, 2, 3, 4), prefix = "<"))
println(joinString(collection = listOf(1, 2, 3, 4), postfix = ">"))
println(joinString(collection = listOf(1, 2, 3, 4)))
}
运行结果:
<1%2%3%4>
<1,2,3,4>
<1!2!3!4]
[1!2!3!4>
[1!2!3!4]
<1,2,3,4]
[1,2,3,4>
[1,2,3,4]
Process finished with exit code 0
四、@JvmOverloads注解解决Java调用Kotlin重载函数问题
由于在Java中是没有默认值参数的概念,当我们需要从Java中调用Kotlin中的默认值重载函数的时候,必须显示的指定所有参数值。但是这个绝对不是我们想要,否则Kotlin就失去了重载的意义了不能和Java完全互操作。所以在Kotlin给出了另一个方案就是使用@JvmOverloads注解这样就会自动生成多个重载方法供Java调用。我可以通过Kotlin代码来看下以下例子
kotlin代码:
@JvmOverloads fun <T> joinString( collection: Collection<T> = listOf(), separator: String = ",", prefix: String = "", postfix: String = "" ): String { return collection.joinToString(separator, prefix, postfix) }
反编译代码成Java代码:
// $FF: synthetic method // $FF: bridge method @JvmOverloads @NotNull public static String joinString$default(Collection var0, String var1, String var2, String var3, int var4, Object var5) { if((var4 & 1) != 0) { var0 = (Collection)CollectionsKt.emptyList(); } if((var4 & 2) != 0) { var1 = ","; } if((var4 & 4) != 0) { var2 = ""; } if((var4 & 8) != 0) { var3 = ""; } return joinString(var0, var1, var2, var3); } @JvmOverloads @NotNull public static final String joinString(@NotNull Collection collection, @NotNull String separator, @NotNull String prefix) { return joinString$default(collection, separator, prefix, (String)null, 8, (Object)null); } @JvmOverloads @NotNull public static final String joinString(@NotNull Collection collection, @NotNull String separator) { return joinString$default(collection, separator, (String)null, (String)null, 12, (Object)null); } @JvmOverloads @NotNull public static final String joinString(@NotNull Collection collection) { return joinString$default(collection, (String)null, (String)null, (String)null, 14, (Object)null); } @JvmOverloads @NotNull public static final String joinString() { return joinString$default((Collection)null, (String)null, (String)null, (String)null, 15, (Object)null); }
五、需要注意的问题
1、在Kotlin中函数使用命名参数需要注意的一点就是,即时在Java重载了很多构造器方法或者普通方法,在Kotlin中调用Java中的方法是不能使用命名参数的,不管你是JDK中的函数或者是Android框架中的函数都是不允许使用命名参数的。
2、在Kotlin中参数的默认值是被编译到被调用的函数中的,而不是调用的地方,所以改变了默认值后需要重新编译这个函数。我们可以从如下反编译代码可以看出Kotlin是把默认值编译进入了函数内部的。
@JvmOverloads @NotNull public static String joinString$default(Collection var0, String var1, String var2, String var3, int var4, Object var5) { if((var4 & 1) != 0) { var0 = (Collection)CollectionsKt.emptyList(); } if((var4 & 2) != 0) { var1 = ","; } if((var4 & 4) != 0) { var2 = ""; } if((var4 & 8) != 0) { var3 = ""; } return joinString(var0, var1, var2, var3); }