Kotlin - 函数调用运算符重载

时间:2017-03-31 21:26:49

标签: operator-overloading kotlin

我目前正在研究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}")
    }
    

    打印:

      

    {&#34; one&#34; ,&#34;两个&#34; ,&#34;三&#34; }
      (myType.contains(&#34; four&#34;))= false,(&#34; my&#34; in myType)= true

测试尝试
基于以上。我尝试使用类型invoke()作为扩展函数(Boolean, Boolean, Boolean) -> Boolean的接收器类型来创建函数的invoke(flag1: Boolean, flag2: Boolean, flag3: Boolean)运算符重载的示例。然而,这并没有像预期的那样发挥作用。

    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 body
  func2 body

预期:

  

重载调用操作符
  重载的调用操作符

另一个问题
究竟是什么? (如果它不是运算符重载)

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

2 个答案:

答案 0 :(得分:10)

如另一个答案所述,invoke是在函数对象本身上定义的,因此你不能用扩展方法覆盖它。

我认为这里更深层次的问题是对此功能的目的略有误解。我们来看看plus运营商的+

我认为您同意尝试定义operator fun Int.plus(b: Int): Int { /* ... */}毫无意义,因为覆盖默认的+运算符是非常危险的,是吗?

但是,如果您定义一个复数类:

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

然后将这个定义为对复数进行求和是完全合理的:

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

因此,与invoke()相同:()的含义是,它类似于为您的类型调用函数,并覆盖invoke on已经是功能的东西只是在寻找麻烦。您想要使用它的目的是为您自己的对象提供类似函数的语法。

例如,假设您定义了如下界面:

interface MyCallback {
  fun call(ctx: MyContext)
}

你使用通常的方法:

callback.call(ctx)

但是通过实现invoke运算符重载,您可以将其用作函数:

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

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

希望澄清您如何使用invoke / ()

答案 1 :(得分:4)

您的问题必须以分辨率优先。 According to the Kotlin docs

  

如果一个类有一个成员函数,并且定义了一个具有相同接收器类型,相同名称且适用于给定参数的扩展函数,则成员总是胜出

因此,永远不会调用operator fun ((Boolean, Boolean, Boolean) -> Boolean).invoke(...)扩展函数,因为成员invoke优先。

另一个答案

确实是运营商重载,但通过扩展。同样,由于(Boolean, Boolean, Boolean) -> Boolean已经定义了fun invoke(Boolean, Boolean, Boolean): Boolean,因此您的扩展程序会丢失。