时间:2021-05-20
前言
在Java中,为函数的参数添加默认值是不被允许的,这是为了防止默认参数与函数重载同时使用时二义性的问题,考虑下面的例子:
void func(p1: String, p2: String, p3: String = "default") { // do something}void func(String p1, String p2) { // do something}假设上面的代码是可以编译通过的,那么当调用func("p1","p2") 时,编译器会不知道到底该调用哪个方法。所以Java是不支持默认参数的,但是依然可以通过函数重载的方式实现默认参数的功能,这也是我们最普遍使用的方式:
void func(String p1, String p2, String p3) { // do something}void func(String p1, String p2) { func(a, b, "default");}通过上述函数重载的方式,也可以实现默认参数,但是有个问题也很明显,就是如果要支持默认参数,我们需要写很多的模版代码,好像也不是那么方便。然而,Kotlin 提供了默认参数的支持,接下来看看Kotlin中对于默认参数的支持是怎样的,又是怎么解决我们开始提到的那个二义性的问题的。
使用
在Kotlin中,使用默认参数也很简单,在函数定义中直接赋值即可:
fun func(p1: String, p2: String, p3: String = "default") { // do something}上述函数定义中,c 的默认值就是default,可以这样去调用 func("p1","p2")。同样的,针对构造函数,也可以指定默认值:
class TestDefaultParameters ( val name: String, val type: String = "default"){}那么如果想要在Java中调用kotlin带有默认参数的函数怎么做呢?如果在Java中直接调用func("p1","p2")编译器会报错,这是需要给kotlin的方法加上Jvm重载的注解就可以了:
@JvmOverloadsfun func(p1: String, p2: String, p3: String = "default") { // do something}解析
接下来,我们看看Kotlin是如何实现默认参数的,首先,写一个例子如下:
fun main(args: Array<String>) { val testDefaultParameters = TestDefaultParameters("") testDefaultParameters.func("position1", "position2")}class TestDefaultParameters ( val name: String, val type: String = "default"){ @JvmOverloads fun func(p1: String, p2: String, p3: String = "default") { // do something }}将上述的func的函数定义Decompile为Java实现:
@JvmOverloadspublic final void func(@NotNull String p1, @NotNull String p2, @NotNull String p3) { Intrinsics.checkParameterIsNotNull(p1, "p1"); Intrinsics.checkParameterIsNotNull(p2, "p2"); Intrinsics.checkParameterIsNotNull(p3, "p3");}// $FF: synthetic methodpublic static void func$default(TestDefaultParameters var0, String var1, String var2, String var3, int var4, Object var5) { if ((var4 & 4) != 0) { var3 = "default"; } var0.func(var1, var2, var3);}@JvmOverloadspublic final void func(@NotNull String p1, @NotNull String p2) { func$default(this, p1, p2, (String)null, 4, (Object)null);}...// 调用func函数TestDefaultParameters.func$default(testDefaultParameters, "position1", "position2", (String)null, 4, (Object)null);中间一些代码我省略了,可以看到,Kotlin编译器为我们生成了三个func的重载方法,下面我们依次来看一下分别都是什么函数:
在了解了三个方法的作用之后,主要来看一下第二个方法:
// $FF: synthetic methodpublic static void func$default(TestDefaultParameters var0, String var1, String var2, String var3, int var4, Object var5) { if ((var4 & 4) != 0) { var3 = "default"; } var0.func(var1, var2, var3);}可以看到这个方法有6个参数,var0为Class对象,var1 ~var3 分别对应func函数的三个参数,然后有一个int类型的var4和一个Object类型的 var5。var5这个大多数情况下都为null,默认参数实现的秘密主要是在这个var 4上, 来看看当调用函数使用默认参数时,是怎么调用的:
// kotlinfunc("position1", "position2") // Decompilefunc$default(testDefaultParameters, "position1", "position2", (String)null, 4, (Object)null)看到var4的值为4。是由于原函数是第三个参数为默认参数,即 position = 2位置的参数,所以 var4 = 222^222=4
在看之前func$default 的方法实现:
当var4 & 4 != 0的时候,var3的值就等于默认参数。可以发现,func$default函数的int类型的参数就是表示第几个参数的值是默认参数的。下面看一个稍微复杂的例子:
fun func(p1: String = "position1", p2: String = "position2", p3: String = "position3") { // do something}// 调用testDefaultParameters.func(p2 = "position2")这次三个参数都有默认值,且调用时用具名参数指定p2的值为"position2"。下面看看Decompile后的代码:
// $FF: synthetic methodpublic static void func$default(TestDefaultParameters var0, String var1, String var2, String var3, int var4, Object var5) {if ((var4 & 1) != 0) { var1 = "position1";}if ((var4 & 2) != 0) { var2 = "position2";}if ((var4 & 4) != 0) { var3 = "position3";} var0.func(var1, var2, var3);}// 调用func$default(testDefaultParameters, (String)null, "position2", (String)null, 5, (Object)null)可以看到,这次方法体内有三个判断,因为有三个参数都是有默认值的,传递的参数为5,是由于函数调用时,index=0 和 index=2的参数为默认参数,所以 var4 = 20+222^0 + 2^220+22 = 5。
这里大概解释一下为什么要这么设计的原因:
当写有多个条件,例如权限判断,index判断等逻辑的时候非常适合位运算。例如在上面的例子中,参数的index可以表示为:2的index幂的二进制数,例如 index = 0 即 202^020 , 用二进制表示为:0001,index = 1 即 212^121 ,表示为:0010(可以看作是二进制数中1的位置,即表示index)。
那么如果多个位置比如index=0与index=2呢?既可以表示为:0101。就是 20+22=52^0 + 2^2 = 520+22=5 。与目标所在的index进行按位与运算的时候,如果不等于0就表示该index符合条件。否则不符合。
回过头来看上述func$default函数体就清晰了,就是通过位置判断,当不是使用默认值的位置时,就不使用默认值。
上述例子中,我们看到含有默认值的参数的函数在Decompile之后,有一个始终为null的Object参数,而且也没有被使用到。那么这个参数有什么用呢?这个参数会在尝试重写有默认参数的函数时用到。例如下面的例子:
open class TestDefaultParameters { open fun func(p1: String = "position1", p2: String = "position2", p3: String = "position3") { // do something }}class TestDefaultChild : TestDefaultParameters() { override fun func(p1: String, p2: String, p3: String) { // do something }}将上述代码编译一下:
// $FF: synthetic methodpublic static void func$default(TestDefaultParameters var0, String var1, String var2, String var3, int var4, Object var5) { if (var5 != null) { throw new UnsupportedOperationException("Super calls with default arguments not supported in this target, function: func"); } else { if ((var4 & 1) != 0) { var1 = "position1"; } if ((var4 & 2) != 0) { var2 = "position2"; } if ((var4 & 4) != 0) { var3 = "position3"; } var0.func(var1, var2, var3);}}可以看到,当调用超类使用默认参数在当前版本是不允许的(以后可能允许)。这就是Object参数的用处。
总结
以上就是对kotlin的默认参数实现的一些总结。
到此这篇关于Kotlin学习教程之函数的默认参数的文章就介绍到这了,更多相关Kotlin函数的默认参数内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
Kotlin基础教程之函数定义与变量声明可以看到,函数定义就是{函数体}单语句函数可以简写,比如add函数和add1函数效果是一样的变量定义var:=常量定义v
kotlin官方学习教程之基础语法详解Google在今天的举行了I/O大会,大会主要主要展示内有容AndroidO(Android8.0)系统、GoogleAs
Kotlin基础教程之反射概述反射是语言与库中的一组功能,可以在运行时刻获取程序本身的信息.在Kotlin中,不仅可以通过发射获取类的信息,同时可以获取函数和属
Kotlin教程之函数和包的定义和流程控制包名的定义在xxx.kt文件的顶部进行定义,使用package关键字,定义后,引用包内函数或类,使用包前缀:packa
Kotlin基础教程之Run,标签Label,函数Function-Type在Java中可以使用{}建立一个匿名的代码块,代码块会被正常的执行,除了改变了作用域