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是不支持默認參數的,但是依然可以通過函數重載的方式實現(xiàn)默認參數的功能,這也是我們最普遍使用的方式:
void func(String p1, String p2, String p3) { // do something } void func(String p1, String p2) { func(a, b, "default"); }
通過上述函數重載的方式,也可以實現(xiàn)默認參數,但是有個問題也很明顯,就是如果要支持默認參數,我們需要寫很多的模版代碼,好像也不是那么方便。然而,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是如何實現(xiàn)默認參數的,首先,寫一個例子如下:
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實現(xiàn):
@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的重載方法,下面我們依次來看一下分別都是什么函數:
- 首先看到的第一個函數是帶有三個參數的func,函數內部都做了空安全的檢查,這是kotlin的特性,由于聲明函數時參數都是不為空的,所以這里需要檢查參數是否為空,會拋出異常。
- 第二個函數我們看到名字是func$default, 并不是func的方法重載,而是一個新的方法,這就是默認參數實現(xiàn)的關鍵方法,這里暫且按下不表,后面詳細講解。
- 第三個函數依然是func方法的重載,可以看到這個方法只有兩個參數,并且內部調用了第二個方法。其實這個方法是給Java調用的,由于我們將func函數聲明為@JvmOverloads,所以當Java在不傳遞默認參數調用func的時候,實際上調用的是這個方法。如果將@JvmOverloads去掉的話,是沒有這個方法的。
在了解了三個方法的作用之后,主要來看一下第二個方法:
// $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,默認參數實現(xiàn)的秘密主要是在這個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 的方法實現(xiàn):
if ((var4 & 4) != 0) { var3 = "default"; }
當var4 & 4 != 0的時候,var3的值就等于默認參數??梢园l(fā)現(xiàn),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的默認參數實現(xiàn)的一些總結。
到此這篇關于Kotlin學習教程之函數的默認參數的文章就介紹到這了,更多相關Kotlin函數的默認參數內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Cocos2d-x 3.0中集成社交分享ShareSDK的詳細步驟和常見問題解決
這篇文章主要介紹了Cocos2d-x 3.0中集成社交分享ShareSDK的詳細步驟和常見問題的解決方法以及需要注意的問題,需要的朋友可以參考下2014-04-04Android編程實現(xiàn)異步消息處理機制的幾種方法總結
這篇文章主要介紹了Android編程實現(xiàn)異步消息處理機制的幾種方法,結合實例形式詳細總結分析了Android異步消息處理機制的原理、相關實現(xiàn)技巧與操作注意事項,需要的朋友可以參考下2018-08-08使用DrawerLayout組件實現(xiàn)側滑抽屜的功能
DrawerLayout組件同樣是V4包中的組件,也是直接繼承于ViewGroup類,所以說是一個容器類,下面通過本文給大家介紹使用DrawerLayout組件實現(xiàn)側滑抽屜的功能,感興趣的朋友一起看下吧2016-08-08詳解Android?Flutter如何使用相機實現(xiàn)拍攝照片
在app中使用相機肯定是再平常不過的一項事情了,相機肯定涉及到了底層原生代碼的調用,那么在flutter中如何快速簡單的使用上相機的功能呢?一起來看看吧2023-04-04