gratis descargar compiler kotlin

descargar - ¿Qué es un "receptor" en Kotlin?



kotlin vs scala (4)

Literales de funciones / Lambda con receptor

Kotlin apoya el concepto de "literales de función con receptores". Permite el acceso a métodos y propiedades visibles de un receptor de una lambda en su cuerpo sin ningún calificador adicional . Esto es muy similar a las funciones de extensión en las que también es posible acceder a miembros visibles del objeto receptor dentro de la extensión.

Se apply un ejemplo simple, también una de las mejores funciones de la biblioteca estándar de Kotlin:

public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

Como puede ver, tal función literal con receptor se toma como el block argumento aquí. Este bloque simplemente se ejecuta y se devuelve el receptor (que es una instancia de T ). En acción, esto se ve de la siguiente manera:

val foo: Bar = Bar().apply { color = RED text = "Foo" }

Instanciamos un objeto de Bar y llamamos a apply . La instancia de Bar convierte en el "receptor". El block , pasado como argumento en {} (expresión lambda) no necesita usar calificadores adicionales para acceder y modificar el color y el text propiedades visibles mostradas.

El concepto de lambdas con receptor también es la característica más importante para escribir DSL con Kotlin.

¿Cómo se relaciona con las funciones de extensión? ¿Por qué es with una función , no una palabra clave?

Parece que no hay documentación explícita para este tema, solo la suposición de conocimiento en referencia a las extensions .


En pocas palabras (sin palabras ni complicaciones adicionales), el "Receptor" es el tipo que se extiende en la función de extensión o el nombre de la clase. Usando los ejemplos dados en las respuestas anteriores

fun Foo.functionInFoo(): Unit = TODO()

Tipo "Foo" es el "receptor"

var greet: String.() -> Unit = { println("Hello $this") }

Tipo "Cadena" es el "Receptor"

Consejo adicional: Esté atento a la clase antes de la parada completa (.) En la declaración de diversión

fun receiver_class.function_name() { //... }


Es cierto que parece haber poca documentación existente para el concepto de receptores (solo una pequeña nota al margen relacionada con las funciones de extensión ), lo cual es sorprendente dado:

Todos estos temas tienen documentación, pero nada profundiza en los receptores.

Primero:

¿Qué es un receptor?

Cualquier bloque de código en Kotlin puede tener un (o incluso varios) tipos como receptor , haciendo que las funciones y propiedades del receptor estén disponibles en ese bloque de código sin calificarlo.

Imagine un bloque de código como este:

{ toLong() }

No tiene mucho sentido, ¿verdad? De hecho, asignar esto a un tipo de función de (Int) -> Long - donde Int es el (único) parámetro y el tipo de retorno es Long - con razón daría lugar a un error de compilación. Puede solucionar esto simplemente calificando la llamada a la función con el parámetro único implícito. Sin embargo, para la construcción de DSL, esto causará un montón de problemas:

  • Los bloques anidados de DSL tendrán sus capas superiores sombreadas:
    html { it.body { // how to access extensions of html here? } ... }
    Esto puede no causar problemas para un HTML DSL, pero puede causar otros casos de uso.
  • Puede ensuciar el código con it llamadas, especialmente para lambdas que usan mucho su parámetro (que pronto será receptor).

Aquí es donde entran en juego los receptores .

Al asignar este bloque de código a un tipo de función que tiene Int como receptor (¡no como parámetro!), El código de repente compila:

val intToLong: Int.() -> Long = { toLong() }

¿Que está pasando aqui?

Una pequeña nota al margen

Este tema supone familiaridad con los tipos de funciones , pero se necesita una pequeña nota al margen para los receptores.

Los tipos de función también pueden tener un receptor, prefijándolo con el tipo y un punto. Ejemplos:

Int.() -> Long // taking an integer as receiver producing a long String.(Long) -> String // taking a string as receiver and long as parameter producing a string GUI.() -> Unit // taking an GUI and producing nothing

Tales tipos de funciones tienen su lista de parámetros prefijada con el tipo de receptor.

Resolviendo código con receptores

En realidad, es increíblemente fácil entender cómo se manejan los bloques de código con receptores:

Imagine que, similar a las funciones de extensión, el bloque de código se evalúa dentro de la clase del tipo de receptor. this efectivamente se modifica por el tipo de receptor.

Para nuestro ejemplo anterior, val intToLong: Int.() -> Long = { toLong() } , resulta efectivamente que el bloque de código se evalúa en un contexto diferente, como si estuviera ubicado en una función dentro de Int . Aquí hay un ejemplo diferente que usa tipos artesanales que lo muestran mejor:

class Bar class Foo { fun transformToBar(): Bar = TODO() } val myBlockOfCodeWithReceiverFoo: (Foo).() -> Bar = { transformToBar() }

efectivamente se convierte (en la mente, no en el código sabio - en realidad no puede extender las clases en la JVM):

class Bar class Foo { fun transformToBar(): Bar = TODO() fun myBlockOfCode(): Bar { return transformToBar() } } val myBlockOfCodeWithReceiverFoo: (Foo) -> Bar = { it.myBlockOfCode() }

Observe cómo, dentro de una clase, no necesitamos usar this para acceder a transformToBar : lo mismo sucede en un bloque con un receptor.

Sucede que la documentación sobre this también explica cómo usar un receptor más externo si el bloque de código actual tiene dos receptores, a través de un calificado .

Espera, ¿múltiples receptores?

Sí. Un bloque de código puede tener múltiples receptores, pero actualmente no tiene expresión en el sistema de tipos. La única forma de archivar esto es a través de múltiples funciones de orden superior que toman un solo tipo de función de receptor. Ejemplo:

class Foo class Bar fun Foo.functionInFoo(): Unit = TODO() fun Bar.functionInBar(): Unit = TODO() inline fun higherOrderFunctionTakingFoo(body: (Foo).() -> Unit) = body(Foo()) inline fun higherOrderFunctionTakingBar(body: (Bar).() -> Unit) = body(Bar()) fun example() { higherOrderFunctionTakingFoo { higherOrderFunctionTakingBar { functionInFoo() functionInBar() } } }

Tenga en cuenta que si esta característica del lenguaje Kotlin parece inapropiada para su DSL, ¡ @DslMarker es su amigo!

Conclusión

¿Por qué importa todo esto? Con este conocimiento:

  • ahora comprende por qué puede escribir toLong() en una función de extensión en un número, en lugar de tener que hacer referencia al número de alguna manera. ¿Quizás su función de extensión no debería ser una extensión?
  • Puede crear un DSL para su lenguaje de marcado favorito, tal vez ayudar a analizar uno u otro (¡¿ quién necesita expresiones regulares?! ).
  • Usted comprende por qué existe una función de biblioteca estándar y no una palabra clave: el acto de enmendar el alcance de un bloque de código para ahorrar en tipeo reductor es tan común que los diseñadores de lenguaje lo ubican en la biblioteca estándar.
  • (tal vez) aprendiste un poco sobre los tipos de funciones en la rama.

var greet: String.() -> Unit = { println("Hello $this") }

esto define una variable de tipo String.() -> Unit , que le dice

  • String es el receptor
  • () -> Unit es el tipo de función

Como mencionó anteriormente, todos los métodos de este receptor se pueden llamar en el cuerpo del método.

Entonces, en nuestro ejemplo, this se usa para imprimir la String . La función se puede invocar escribiendo ...

greet("Fitzgerald") // result is "Hello Fitzgerald"

el fragmento de código anterior fue tomado de Kotlin Function Literals with Receiver - Introducción rápida por Simon Wirtz.