Kotlin. Lambda vs Funktionsreferenz

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



  1. FirstFirst







  2. FirstSecond







  3. SecondFirst







  4. SecondSecond







Antworten

3





Danke fürs Lesen, ich hoffe jemand war interessiert und nützlich!








All Articles