Kotlin ist seit langem die gängige Programmiersprache für Android. Einer der Gründe, warum ich diese Sprache mag, ist, dass die Funktionen darin erstklassige Objekte sind . Das heißt, eine Funktion kann als Parameter übergeben, als Rückgabewert verwendet und einer Variablen zugewiesen werden. Anstelle einer Funktion können Sie auch ein sogenanntes Lambda übergeben . Und kürzlich hatte ich ein interessantes Problem im Zusammenhang mit dem Ersetzen des Lambda durch eine Funktionsreferenz.
Stellen Sie sich vor, wir haben eine Klasse Button
, die eine Funktion im Konstruktor als Parameter empfängtonClick
class Button(
private val onClick: () -> Unit
) {
fun performClick() = onClick()
}
Und es gibt eine Klasse ButtonClickListener
, die die Logik von Tastenklicks implementiert
class ButtonClickListener {
fun onClick() {
print(" ")
}
}
In der Klasse ScreenView
speichern wir eine Variable lateinit var listener: ButtonClickListener
und erstellen eine Schaltfläche, der ein Lambda übergeben wird, in dem die Methode aufgerufen wirdButtonClickListener.onClick
class ScreenView {
lateinit var listener: ButtonClickListener
val button = Button { listener.onClick() }
}
In der Methode main
erstellen wir ein Objekt ScreenView
, initialisieren die Variable listener
und simulieren das Klicken auf die Schaltfläche
fun main() {
val screenView = ScreenView()
screenView.listener = ButtonClickListener()
screenView.button.performClick()
}
Nach dem Start der Anwendung funktioniert alles einwandfrei und die Zeile "Taste gedrückt" wird angezeigt.
Kehren wir nun zur Klasse zurück ScreenView
und schauen uns die Zeile an, in der die Schaltfläche erstellt wurde val button = Button { listener.onClick() }
. Möglicherweise haben Sie bemerkt, dass die Methode ButtonClickListener.onClick
in ihrer Signatur der Funktion ähnelt, die onClick: () -> Unit
der Konstruktor unserer Schaltfläche übernimmt. Dies bedeutet, dass wir den Lambda-Ausdruck durch eine Funktionsreferenz ersetzen können. Als Ergebnis bekommen wir
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;
, , , , , , .
Dies führt zu folgendem interessanten Problem: Was wird nach Programmstart gedruckt?
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
Antworten
3
Danke fürs Lesen, ich hoffe jemand war interessiert und nützlich!