Kotlin. Référence Lambda vs Function

Kotlin est depuis longtemps le langage de programmation grand public sur Android. Une des raisons pour lesquelles j'aime ce langage est que ses fonctions sont des objets de première classe . Autrement dit, une fonction peut être transmise en tant que paramètre, utilisée comme valeur de retour et affectée à une variable. En outre, au lieu d'une fonction, vous pouvez passer un soi-disant lambda . Et récemment, j'ai eu un problème intéressant lié au remplacement du lambda par une référence de fonction.





Imaginons que nous ayons une classe Button



qui reçoit une fonction dans le constructeur en tant que paramètreonClick







class Button(
    private val onClick: () -> Unit
) {
    fun performClick() = onClick()
}
      
      



Et il existe une classe ButtonClickListener



qui implémente la logique des clics sur les boutons





class ButtonClickListener {
    fun onClick() {
        print(" ")
    }
}
      
      



Dans la classe ScreenView



, nous stockons une variable lateinit var listener: ButtonClickListener



et créons un bouton, auquel est passé un lambda, à l'intérieur duquel la méthode est appeléeButtonClickListener.onClick







class ScreenView {
    lateinit var listener: ButtonClickListener
    val button = Button { listener.onClick() }
}
      
      



Dans la méthode, main



nous créons un objet ScreenView



, initialisons la variable listener



et simulons un clic sur le bouton





fun main() {
    val screenView = ScreenView()
    screenView.listener = ButtonClickListener()
    screenView.button.performClick()
}
      
      



Après le démarrage de l'application, tout fonctionne bien et la ligne «Bouton enfoncé» s'affiche.





Revenons maintenant à la classe ScreenView



et regardons la ligne où le bouton est créé - val button = Button { listener.onClick() }



. Vous avez peut-être remarqué que la méthode ButtonClickListener.onClick



est similaire en signature à la fonction prise onClick: () -> Unit



par le constructeur de notre bouton, ce qui signifie que nous pouvons remplacer l'expression lambda par une référence de fonction. En conséquence, nous obtenons





class ScreenView {
    lateinit var listener: ButtonClickListener
    val button = Button(listener::onClick)
}
      
      



- listener





Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property listener has not been initialized
at lambdas.ScreenView.<init>(ScreenView.kt:6)
at lambdas.ScreenViewKt.main(ScreenView.kt:10)
at lambdas.ScreenViewKt.main(ScreenView.kt)
      
      



, Java . .





Function0



invoke



, . - listener.onClick()







private final Button button = new Button((Function0)(new Function0() {
    public final void invoke() {
       ScreenView.this.getListener().onClick();
    }
}));
      
      



, listener



.





. Function0



, invoke()



, , onClick



this.receiver



. receiver



Function0



listener



, listener



lateinit



, receiver



- listener



null



, . .





Button var10001 = new Button;
Function0 var10003 = new Function0() {
   public final void invoke() {
      ((ButtonClickListener)this.receiver).onClick();
   }
};
ButtonClickListener var10005 = this.listener;
if (var10005 == null) {
   Intrinsics.throwUninitializedPropertyAccessException("listener");
}

var10003.<init>(var10005);
var10001.<init>((Function0)var10003);
this.button = var10001;
      
      



, , , , , , .





Cela conduit au problème intéressant suivant: Qu'est-ce qui sera imprimé après le démarrage du programme?





class Button(
    private val onClick: () -> Unit
) {
    fun performClick() = onClick()
}

class ButtonClickListener(
    private val name: String
) {
    fun onClick() {
        print(name)
    }
}

class ScreenView {
    var listener = ButtonClickListener("First")
    val buttonLambda = Button { listener.onClick() }
    val buttonReference = Button(listener::onClick)
}

fun main() {
    val screenView = ScreenView()
    screenView.listener = ButtonClickListener("Second")
    screenView.buttonLambda.performClick()
    screenView.buttonReference.performClick()
}
      
      



  1. FirstFirst







  2. FirstSecond







  3. SecondFirst







  4. SecondSecond







Répondre

3





Merci d'avoir lu, j'espère que quelqu'un a été intéressé et utile!








All Articles