Definición de la function: fun vs val

Tengo curiosidad sobre cuál es la forma sugerida de definir las funciones de miembros en Kotlin. Considere estas dos funciones miembro:

class A { fun f(x: Int) = 42 val g = fun(x: Int) = 42 } 

Estos parecen lograr lo mismo, pero encontré diferencias sutiles.

La definición basada en val , por ejemplo, parece ser más flexible en algunos escenarios. Es decir, no pude encontrar una manera directa de componer f con otras funciones, pero pude con g . Para jugar con estas definiciones, utilicé la biblioteca funKTionale . Descubrí que esto no comstack:

 val z = g andThen A::f // f is a member function 

Pero si f se definiera como un val apunte a la misma function, se comstackría muy bien. Para descubrir qué estaba pasando le pedí a IntelliJ que definiera explícitamente el tipo de ::f g para mí, y me da esto:

 val fref: KFunction1<Int, Int> = ::f val gref: (Int) -> Int = g 

Entonces uno es del tipo KFunction1<Int, Int> , el otro es del tipo (Int) -> Int . Es fácil ver que ambos representan funciones de tipo Int -> Int .

¿Cuál es la diferencia entre estos dos types y en qué casos importa? Noté que para las funciones de nivel superior, puedo componerlas bien usando cualquiera de las definiciones, pero para hacer la compilation de composition antes mencionada, tuve que escribirla así:

 val z = g andThen A::f.partially1(this) 

es decir, tuve que aplicarlo parcialmente a this primero.

Como no tengo que pasar por esta molestia cuando uso val para las funciones, ¿hay alguna razón por la que alguna vez deba definir las funciones de los miembros que no son de la Unidad utilizando la fun ? ¿Hay alguna diferencia en el performance o la semántica que me falta?

Kotlin tiene que ver con la interoperabilidad de Java y definir una function como val producirá un resultado completamente diferente en términos de interoperabilidad. La siguiente class de Kotlin:

 class A { fun f(x: Int) = 42 val g = fun(x: Int) = 42 } 

es efectivamente equivalente a:

 public class A { private final Function1<Integer, Integer> gref = new Function1<Integer, Integer>() { @Override public Integer invoke(final Integer integer) { return 42; } }; public int f(final int value) { return 42; } public Function1<Integer, Integer> getG() { return gref; } } 

Como puede ver, las principales diferencias son:

  1. fun f es solo un método habitual, mientras que val g en realidad es una function de order superior que devuelve otra function
  2. val g implica la creación de una nueva class que no es buena si estás apuntando a Android
  3. val g requiere un boxeo y unboxing innecesarios
  4. val g no se puede invocar fácilmente desde java: A().g(42) en Kotlin vs new A().getG().invoke(42) en Java

ACTUALIZAR:

En cuanto a la syntax A::f . El comstackdor generará una class adicional Function2<A, Integer, Integer> para cada ocurrencia A::f , por lo que el siguiente código da como resultado dos classs extra con 7 methods cada una:

 val first = A::f val second = A::f 

El comstackdor de Kotlin no es lo suficientemente inteligente en este momento para optimizar este tipo de cosas. Puede votar por el problema aquí https://youtrack.jetbrains.com/issue/KT-9831 . En caso de que le interese, así es como se ve cada class en el bytecode: https://gist.github.com/nsk-mironov/fc13f2075bfa05d8a3c3

Aquí hay un código que muestra cómo f y g son diferentes cuando se trata de uso:

 fun main(args: Array<String>) { val a = A() exe(ag) // OK //exe(af) // does not compile exe { af(it) } // OK } fun exe(p: (Int) -> Int) { println(p(0)) } 

Puedes ver que g es un object que puede usarse como un lambda, pero f no puede. Para usar f de manera similar, debes envolverlo en una lambda.

  • Kotlin: ¿cómo usar múltiples generics en class?
  • ¿Hay alguna forma de reutilizar una instancia de Job?
  • ¿Por qué este método de Kotlin tiene palos de respaldo?
  • ¿Cómo adjunto fonts para Kotlin en Android Studio?
  • Retrollamada Glide después del éxito en Kotlin
  • Diseño inflado por progtwigción con las extensiones de Kotlin para Android
  • android-kotlin error: databinding de reference no resuelto & ActivityMainBinding
  • Kotlin Vertx Type Mismatch encontrado Future <Unit> expected Handler <AsyncResult <Void >>