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()
}
FirstFirst
FirstSecond
SecondFirst
SecondSecond
Répondre
3
Merci d'avoir lu, j'espère que quelqu'un a été intéressé et utile!