2017-03-31 33 views
2

目前我正在Kotlin - Operator Overloading
我想了解(用一個例子)的函數的invoke()功能科特林 - 函數調用運算符重載

預測試如何操作符重載工作

  • Kotlin's Extension Functions

    fun exampleOfExtensionFunction() { 
        fun Int.randomize(): Int { 
         return Random(this.toLong()).nextInt() 
        } 
    
        val randomizedFive = 5.randomize() 
        println("$randomizedFive") 
    } 
    

    打印:

    -1157408321

  • In Kotlin, functions can be declared as variables with types

    fun exampleOfFunctionType() { 
        val printNumber: (number: Int) -> Unit 
        printNumber = { number -> 
         println("[$number = ${number.toString(16).toUpperCase()} = ${number.toString(2)}]") 
        } 
    
        printNumber(1023) 
    } 
    

    打印:

    [1023 = 3FF = 1111111111]

  • Kotlin allows operator overloading with both extension and member functions

    fun exampleOfOperatorOverloadingUsingExtensionFunction() { 
        class MyType() { 
         val strings: ArrayList<String> = ArrayList<String>() 
         override fun toString(): String { 
          val joiner: StringJoiner = StringJoiner(" , ", "{ ", " }") 
          for (string in strings) { 
           joiner.add("\"$string\"") 
          } 
          return joiner.toString() 
         } 
        } 
    
        operator fun MyType.contains(stringToCheck: String): Boolean { 
         for (stringElement in strings) { 
          if (stringElement == stringToCheck) return true 
         } 
         return false 
        } 
    
        val myType = MyType() 
        myType.strings.add("one") 
        myType.strings.add("two") 
        myType.strings.add("three") 
        println("$myType") 
        println("(myType.contains(\"four\")) = ${myType.contains("four")} , (\"three\" in myType) = ${"three" in myType}") 
    } 
    

    打印:

    { 「一」, 「二」, 「三」}
    (myType.contains( 「四」))=假,(」三個」的myType)=真

測試嘗試
根據以上所述。我嘗試使用類型(Boolean, Boolean, Boolean) -> Boolean作爲擴展函數invoke(flag1: Boolean, flag2: Boolean, flag3: Boolean)的接收器類型創建函數invoke()運算符重載的示例。然而,這並沒有按預期那樣工作。

fun attemptFunctionInvokeOperatorOverloading() { 
     operator fun ((Boolean, Boolean, Boolean) -> Boolean).invoke(flag1: Boolean, flag2: Boolean, flag3: Boolean): Boolean { 
      println("Overloaded invoke operator") 
      return flag1 && flag2 && flag3 
     } 

     var func1: ((Boolean, Boolean, Boolean) -> Boolean) = { flag1, flag2, flag3 -> 
      println("func1 body") 
      flag1 && flag2 && flag3 
     } 

     fun func2(flag1: Boolean, flag2: Boolean, flag3: Boolean): Boolean { 
      println("func2 body") 
      return flag1 && flag2 && flag3 
     } 

     func1(true, true, false) 
     func2(true, true, true) 
    } 

打印:

FUNC1體
FUNC2體

預計:

重載調用運營商
重載調用操作

另一個問題
究竟這是什麼道理? (如果不是運營商超載)

 operator fun ((Boolean, Boolean, Boolean) -> Boolean).invoke(flag1: Boolean, flag2: Boolean, flag3: Boolean): Boolean { 
      println("Overloaded invoke operator") 
      return flag1 && flag2 && flag3 
     } 

回答

7

正如在另一個答案中所說的,invoke定義在函數對象本身上,所以你不能用擴展方法覆蓋它。

我認爲這裏的更深層的問題是對這個功能目的的一個輕微的誤解。我們來看plus代替+運算符。

我認爲你會同意試圖定義fun Int.plus(b: Int): Int { /* ... */}是沒有意義的,因爲重寫默認的+運算符是一個非常危險的事情,是的?

但是,如果你定義一個複數類:

data class Complex(real: Double, img: Double) 

然後,它是完全合理界定這個總結複數:

fun Complex.plus(other: Complex): Complex { 
    val real = this.real + other.real 
    val img = this.img + other.img 
    return Complex(real, img) 
} 

所以,用invoke()同樣的事情:將()的含義在於它與爲你的類型調用某個函數類似,並且在已經是函數的東西上覆蓋invoke只是要求麻煩。您想要使用它的目的是爲您自己的對象提供類似功能的語法。

例如,假設你定義一個接口,像這樣:

interface MyCallback { 
    fun call(ctx: MyContext) 
} 

您使用通常的方式:

callback.call(ctx) 

但隨着invoke運算符重載的實現,你能使用它作爲一個功能:

fun MyCallback.invoke(ctx: Context) = this.call(ctx) 

/* Elsewhere... */ 
callback(ctx) 

希望澄清你如何se invoke/()

3

您的問題必須以解決方案優先。According to the Kotlin docs

如果一個類的成員函數,以及擴展函數被定義,其具有相同的接收器類型,相同的名稱和適用於給定的參數,該構件總是獲勝

所以你operator fun ((Boolean, Boolean, Boolean) -> Boolean).invoke(...)擴展功能不會被調用,因爲成員invoke優先。

另一個答案

這的確是運算符重載,而是通過一個擴展。再次,因爲(Boolean, Boolean, Boolean) -> Boolean已經定義了fun invoke(Boolean, Boolean, Boolean): Boolean,所以您的擴展失敗。

+2

原因是,如果擴展的優先級高於成員函數,函數調用的結果可能是不確定的,並且可以通過簡單地在您的項目中包含另一個庫而不需要知道(假定擴展名在範圍內或導入)。 –

+0

這是否意味着無法有效地重載調用函數?因爲每次成員函數都優先。而且因爲沒有辦法在kotlin中定義一個函數類型的類,所以實現運算符重載作爲函數的成員函數。 – naaz

+1

對於函數類型是的,但任何具有開放調用函數的類都可以被覆蓋。功能類型的要點是接受與簽名匹配的任何內容。和其他任何東西一樣,如果你想要特定的功能,你可以創建一個具有該功能的類。 –