117 votes

Qu'est-ce qu'un "récepteur" chez Kotlin?

Comment est-ce lié aux fonctions d'extension? Pourquoi with une fonction , pas un mot clé?

Il semble n'y avoir aucune documentation explicite sur ce sujet, seulement l'hypothèse de connaissance en référence à des extensions .

168voto

F. George Points 2187

Il est vrai qu'il semble y avoir peu de documentation existante pour le concept de récepteurs (seulement une petite remarque concernant les fonctions d'extension), ce qui est surprenant vu:

Tous ces sujets ont de la documentation, mais rien ne va plus en profondeur sur les récepteurs.


D'abord:

Qu'est ce qu'un récepteur?

Tout bloc de code dans Kotlin peut avoir un (ou même plusieurs) types de comme un récepteur, les fonctions et les propriétés du récepteur disponibles dans ce bloc de code sans qualifiying il.

Imaginez un bloc de code comme ceci:

{ toLong() }

N'a pas beaucoup de sens, pas vrai? En fait, l'attribution à un type de fonction de l' (Int) -> Long - où Int le (seul) paramètre et le type de retour est - Long - serait à juste titre, d'une erreur de compilation. Vous pouvez résoudre ce problème simplement en qualifiant l'appel de la fonction avec l'implicite seul paramètre it. Cependant, pour le DSL de construction, ce qui va causer un tas de questions:

  • Les blocs imbriqués de la liste auront couches supérieures de l'ombre:
    html { it.body { // how to access extensions of html here? } ... }
    Cela peut ne pas causer des problèmes pour un HTML DSL, mais peut, pour d'autres cas d'utilisation.
  • Il peut litière avec le code it des appels, en particulier pour les lambdas utilisation de leurs paramètres (bientôt récepteur) d'un lot.

C'est là que les récepteurs entrent en jeu.

Par l'attribution de ce bloc de code d'une fonction d'un type qui a Int comme un récepteur (et non comme un paramètre!), le code soudain compile:

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

Ce qui se passe ici?


Un peu de passage

Cette rubrique suppose familarity avec des types de fonction, mais une petite note de côté pour les récepteurs est nécessaire.

Des types de fonction peut également avoir un récepteur, en préfixant avec le type et d'un point. Exemples:

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

Ces types de fonctions ont leur liste de paramètres préfixé avec le type de récepteur.


La résolution de code avec des récepteurs

Il est en fait incroyablement facile de comprendre comment les blocs de code avec les récepteurs sont traitées:

Imaginez que, semblables à des fonctions d'extension, le bloc de code est évalué à l'intérieur de la classe du type de récepteur. ce fait devient modifié par le type de récepteur.

Pour notre exemple précédent, val intToLong: Int.() -> Long = { toLong() } , il entraîne effectivement dans le bloc de code en cours d'évaluation dans un contexte différent, comme s'il était placé dans une fonction à l'intérieur d' Int. Voici un autre exemple d'utilisation de types fabriqués à la main qui démontre le mieux:

class Bar

class Foo {
    fun transformToBar(): Bar = TODO()
}

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

devient (dans l'esprit, pas de code sages - vous ne pouvez pas réellement étendre des classes sur la JVM):

class Bar 

class Foo {
    fun transformToBar(): Bar = TODO()

    fun myBlockOfCode(): Bar { return transformToBar() }
}

val myBlockOfCodeWithReceiverFoo: (Foo) -> Bar = { it.myBlockOfCode() }

Remarquez comment, à l'intérieur d'une classe, nous n'avons pas besoin d'utiliser this accès transformToBar - la même chose se produit dans un bloc avec un récepteur.

Il se trouve que la documentation sur ce explique également comment utiliser un ultrapériphériques récepteur si le bloc de code a deux récepteurs, par l'intermédiaire d'un qualifié ce.


Attendre, plusieurs récepteurs?

Oui. Un bloc de code peut avoir plusieurs récepteurs, mais actuellement, cela n'a pas d'expression dans le système de type. La seule façon d'obtenir ce par le biais de multiples fonctions d'ordre supérieur que de prendre un seul récepteur type de fonction. Exemple:

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

Notez que si cette caractéristique de la Kotlin langue semble inapproprié pour votre DSL, @DslMarker est votre ami!


Conclusion

Pourquoi toute cette affaire? Avec cette connaissance:

  • vous comprenez maintenant pourquoi vous pouvez écrire toLong() dans une extension de la fonction d'un nombre, au lieu d'avoir pour référence le nombre en quelque sorte. Peut-être que votre fonction d'extension ne doit pas être une extension?
  • Vous pouvez créer un DSL pour votre favori markup language, peut-être aider à l'analyse de l'une ou de l'autre (qui a besoin des expressions régulières?!).
  • Vous comprenez pourquoi with, une bibliothèque standard de la fonction et non un mot-clé, existe - la modification de la portée d'un bloc de code pour enregistrer sur devenus inutiles de frappe est si commun, la langue des designers de le mettre dans la bibliothèque standard.
  • (peut-être) que vous avez appris un peu plus sur la fonction des types sur la ramification.

19voto

s1m0nw1 Points 21698

Des Littéraux de fonction/Lambda avec Récepteur

Kotlin soutient le concept de "fonction littéraux avec des récepteurs". Il permet l'accès visible sur les méthodes et les propriétés d'un récepteur d'un lambda dans son corps , sans autres qualificatifs. Ceci est très similaire à l'extension de fonctions dans lequel il est également possible d'accéder visible des membres de l'objet récepteur à l'intérieur de l'extension.

Un exemple simple, aussi l'une des plus grandes fonctions dans le Kotlin de la bibliothèque standard, est -apply:

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

Comme vous pouvez le voir, une telle fonction littérale avec récepteur est prise comme argument block ici. Ce bloc est simplement exécuté et le récepteur (qui est une instance de l' T) est retourné. Dans cette action se présente comme suit:

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

Nous instancions un objet d' Bar et appelez - apply sur il. L'instance de Bar devient le "récepteur". L' block, passé comme un argument en {}(expression lambda) n'a pas besoin d'utiliser d'autres qualificatifs pour accéder et modifier l'montré les propriétés visibles color et text.

Le concept de lambdas avec récepteur est aussi la caractéristique la plus importante pour l'écriture de DSLs avec Kotlin.

17voto

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

ceci définit une variable de type String.() -> Unit, ce qui en dit long

  • String est le récepteur
  • () -> Unit est le type de fonction

Comme F. George mentionné ci-dessus, toutes les méthodes de ce récepteur peut être appelée dans le corps de la méthode.

Ainsi, dans notre exemple, this est utilisée pour imprimer l' String. La fonction peut être appelée par l'écriture...

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

l'extrait de code ci-dessus a été prise à partir de Kotlin Littéraux de Fonction avec Récepteur – Introduction Rapide par Simon Wirtz.

13voto

GraSim Points 41

En termes simples (sans mots ni complications supplémentaires), le "Récepteur" est le type à étendre dans la fonction d’extension ou le nom de la classe. En utilisant les exemples donnés dans les réponses ci-dessus

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

Le type "Foo" est le "récepteur"

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

Le type "String" est le "récepteur"

Conseil supplémentaire: recherchez la classe avant l’arrêt complet (.) Dans la déclaration de plaisir

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X