2016-09-27 17 views
7

在科特林有兩種方式來表達一個可選參數,通過指定默認參數值:默認參數VS過載,當要使用的

fun foo(parameter: Any, option: Boolean = false) { ... } 

或通過引入一個過載:

fun foo(parameter: Any) = foo(parameter, false) 
fun foo(parameter: Any, option: Boolean) { ... } 

哪種方式更適合哪種情況?

此功能的消費者有什麼區別?

+0

注:雖然有涉及到像其他語言[C#](http://stackoverflow.com/questions/16789341/function-overloading-vs-optional-parameters)或類似的問題[VB.NET] (http://stackoverflow.com/questions/304389/function-overloading-vs-default-parameters-in-vb-net),這個問題是特定於Kotlin。 – Ilya

+0

永遠不要使用重載,就是這樣。 '@ JVMOverloads'可能對從Java使用它們很有用 – voddan

回答

9

在調用其他Kotlin代碼的Kotlin代碼中,可選參數往往是使用重載的標準。使用可選參數應該是你的默認行爲。

特殊情況使用默認的值:

  • 按照一般的做法,或者如果不確定 - 使用默認參數,也覆蓋。

  • 如果您希望調用者看到默認值,請使用默認值。它們將顯示在IDE工具提示中(即Intellij IDEA),並讓來電者知道它們是作爲合同的一部分應用的。您可以在下面的截圖中看到,調用foo()將默認一些數值,如果數值被設置爲xy省略:

    enter image description here

    而具有功能重載做同樣的事情隱藏了有用的信息,只是提出了一個更凌亂:使用默認值

    enter image description here

  • 導致字節碼生成的兩個功能,一個與指定的所有參數和另一第at是一個橋樑功能,可以使用缺省值檢查並應用缺少的參數。無論您有多少違約參數,它總是只有兩個功能。因此,在總功能數量受限的環境中(即Android),最好只有這兩個功能,而不是完成相同工作所需的大量重載。

某些情況下可能不希望使用默認參數值:

  • 如果想讓其他JVM語言可以使用您可能需要使用顯式重載的違約值或使用@JvmOverloads annotation其中:

    對於每個具有默認值的參數,這將生成一個額外的重載,它具有此參數d刪除參數列表中的所有參數。

  • 您有以前版本的庫和二進制API兼容性添加默認參數可能會破壞現有編譯代碼的兼容性,而添加重載不會。

  • 你有以前的現有功能:

    fun foo() = ... 
    

    ,你需要保留該功能簽名,但你也想用相同的簽名,但附加的可選參數添加另一個:

    fun foo() = ... 
    fun foo(x: Int = 5) = ... // never can be called using default value 
    

    您將無法使用第二個版本的默認值(除了通過反射callBy)。相反,所有不帶參數的調用foo()仍然調用該函數的第一個版本。所以,你需要改用不同的重載沒有默認,否則你會混淆功能的用戶:

    fun foo() = ... 
    fun foo(x: Int) = ... 
    
  • 你有觀點認爲可能是沒有意義在一起,因此重載讓你組參數爲有意義的協調組。

  • 具有默認值的調用方法必須執行另一步來檢查缺少哪些值並應用默認值,然後將調用轉發給實際方法。因此,在性能受限的環境中(,即在方法調用上的Android,嵌入式,實時,十億循環迭代),可能不需要此額外檢查。儘管如果您在分析中沒有看到問題,這可能是一個虛構的問題,可能由JVM內聯,並且可能根本沒有任何影響。在擔心之前先測量。

並不真正支持這兩種情況下例:

如果你正在閱讀這個來自其他語言的一般參數...

  • 的尊敬一個C# answer for this similar question喬恩Skeet提到你應該小心使用默認值,如果他們之間可以更改構建之間,這將是一個問題。在C#中,默認值位於調用位置,而Kotlin中的非內聯函數則位於調用的(橋接)函數內部。因此,對Kotlin來說,改變隱藏和顯式的價值違約是同樣的影響,這個論點不應該影響決策。

  • 也在C#答案中說,如果團隊成員對使用默認參數持反對意見,那麼也許不要使用它們。這不應該適用於Kotlin,因爲它們是核心語言功能,並且自1.0之前就在標準庫中使用,並且不支持限制它們的使用。對方團隊成員應該默認使用違約論點,除非他們有明確的案例使其無法使用。而在C#這是很久以後介紹了該語言的生命週期,因此有更多的「可選採用」

+0

我不知道Kotlin,但可以在傳遞非默認Y值的同時,在callin foo()時保留X默認值?如果不是這樣,那麼您需要在代碼中的多個位置複製X的默認值。 –

+1

@BobBrinks在Kotlin文檔中清楚地說明了這一點,您可以傳遞一些值而不是其他值。您可以根據定義參數列表的方式使用有序參數或命名參數。 –

3

感讓我們來看看如何使用默認參數值的功能在科特林被編譯,看是否有一個方法計數的差異。它可能根據目標平臺而有所不同,因此我們首先會考慮Kotlin for JVM。

對於產生以下兩種方法的功能fun foo(parameter: Any, option: Boolean = false)

  • 首先是foo(Ljava/lang/Object;Z)V當在調用點指定的所有論據被調用。
  • 其次是synthetic bridge foo$default(Ljava/lang/Object;ZILjava/lang/Object;)V。它有兩個附加參數:Int掩碼,它指定哪些參數實際上已通過,以及當前未使用的Object參數,但保留用於未來允許使用默認參數的超級調用。

在呼叫站點省略某些參數時會調用該橋。網橋分析掩碼,爲省略參數提供默認值,然後調用現在指定所有參數的第一個方法。

當您將@JvmOverloads註釋放在函數上時,會生成額外的重載,每個參數具有默認值一個。所有這些超載委託給foo$default橋。對於foo函數,將生成以下附加的過載:foo(Ljava/lang/Object;)V

因此,從方法計數的角度來看,在函數只有一個帶有默認值的參數的情況下,無論您使用重載還是默認值,都會得到兩種方法。但是,如果有多個可選參數,則使用默認值而不是重載將導致生成更少的方法。

2

當參數被省略時,函數的實現變得更簡單時,重載可能是首選。

請看下面的例子:

fun compare(v1: T, v2: T, ignoreCase: Boolean = false) = 
    if (ignoreCase) 
     internalCompareWithIgnoreCase(v1, v2) 
    else 
     internalCompare(v1, v2) 

當它被稱爲像compare(a, b)ignoreCase被省略,您實際支付兩次不使用ignoreCase:第一種是當參數進行檢查和默認值被取代,而不是省略第二個是當您檢查compare正文中的ignoreCase並根據其值分支到internalCompare

添加超載將擺脫這兩個檢查。 JIT編譯器也可以將這種簡單機構的方法內聯。

fun compare(v1: T, v2: T) = internalCompare(v1, v2)