Kotlin – Sobrecarga del operador de invocación de la function

Actualmente estoy estudiando Kotlin – Sobrecarga del operador
Estoy intentando comprender (con un ejemplo) cómo funciona la sobrecarga del operador para la function invoke() function

La testing previa

  • Funciones de extensión de Kotlin

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

    Impresiones:

    -1157408321

  • En Kotlin, las funciones se pueden declarar como variables con types

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

    Impresiones:

    [1023 = 3FF = 1111111111]

  • Kotlin permite la sobrecarga del operador con funciones tanto de extensión como de miembro

     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}") } 

    Impresiones:

    { "uno dos tres" }
    (myType.contains ("four")) = false, ("tres" en myType) = true

Intento de testing
Basado en lo anterior. Intenté crear un ejemplo de la sobrecarga del operador invoke() de una function usando el tipo (Boolean, Boolean, Boolean) -> Boolean como un tipo de receptor para una invoke(flag1: Boolean, flag2: Boolean, flag3: Boolean) function de extensión invoke(flag1: Boolean, flag2: Boolean, flag3: Boolean) . Sin embargo, esto no funcionó como se esperaba.

  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) } 

Impresiones:

cuerpo func1
cuerpo func2

Esperado :

Operador de invocación sobrecargado
Operador de invocación sobrecargado

Otra pregunta :
¿Qué es esto exactamente? (Si no es sobrecarga del operador)

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

Como se dijo en otra respuesta, invocar se define en el object de function en sí, por lo que no puede anularlo con un método de extensión.

Creo que el problema más profundo aquí, sin embargo, es un ligero malentendido sobre el propósito de esta característica. Veamos el plus para el operador + lugar.

Creo que estarás de acuerdo en que tratar de definir fun Int.plus(b: Int): Int { /* ... */} no tiene sentido, porque anular el operador pnetworkingeterminado + para las inputs es algo bastante peligroso de hacer, ¿sí?

Sin embargo, si defines una class numérica compleja:

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

Entonces, es perfectamente razonable definir esto para sumr numbers complejos:

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

Entonces, lo mismo con invoke y () : El significado de () es que es lo que sea análogo a invocar una function para su tipo, y anular la invoke sobre algo que ya es una function es solo pedir problemas. En cambio, lo que quiere usar es proporcionar una syntax similar a la function para sus propios objects.

Por ejemplo, imagine que define una interfaz como esta:

 interface MyCallback { fun call(ctx: MyContext) } 

Que usas de la manera habitual:

 callback.call(ctx) 

Pero con una implementación de la sobrecarga del operador de invoke , puede usarlo como una function:

 fun MyCallback.invoke(ctx: Context) = this.call(ctx) /* Elsewhere... */ callback(ctx) 

Espero que aclare cómo usas invoke / ()

Tu problema tiene que tener prioridad de resolución. De acuerdo con los documentos de Kotlin :

Si una class tiene una function miembro, y se define una function de extensión que tiene el mismo tipo de receptor, el mismo nombre y es aplicable a arguments dados, el miembro siempre gana .

Por lo tanto, nunca se llama a la function de extensión operator fun ((Boolean, Boolean, Boolean) -> Boolean).invoke(...) porque la invoke del miembro tiene prioridad.

Otra respuesta

De hecho, es la sobrecarga del operador, pero a través de una extensión. Nuevamente, dado que (Boolean, Boolean, Boolean) -> Boolean ya tiene una fun invoke(Boolean, Boolean, Boolean): Boolean defined, su extensión pierde.