Android

关注公众号 jb51net

关闭
首页 > 软件编程 > Android > Kotlin函数的默认参数

Kotlin学习教程之函数的默认参数

作者:fb0122

这篇文章主要给大家介绍了关于Kotlin学习教程之函数的默认参数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

​ 在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重载的注解就可以了:

@JvmOverloads
fun 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实现:

@JvmOverloads
public 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 method
public 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);
}

@JvmOverloads
public 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 method
public 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上, 来看看当调用函数使用默认参数时,是怎么调用的:

// kotlin
func("position1", "position2")
 
// Decompile
func$default(testDefaultParameters, "position1", "position2", (String)null, 4, (Object)null)

看到var4的值为4。是由于原函数是第三个参数为默认参数,即 position = 2位置的参数,所以 var4 = 222^222=4
在看之前func$default 的方法实现:

if ((var4 & 4) != 0) {
 var3 = "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 method
public 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 method
public 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函数的默认参数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文