Java-Lambda-Ausdrücke (Kotlin / JVM) sind nicht immer separate Instanzen

Beim Schreiben eines Lambda-Ausdrucks anstelle der anonymen inneren Klasse von Java ging ich davon aus, dass es sich um eine separate Instanz handelt. Dies ist ein Memo, das bei der Verwaltung jeder Instanz zu einem Problem wurde.

Um zu erkennen, dass mithilfe von LiveData von Android-Architekturkomponenten eine Datenquelle bereitgestellt wird, die von einem LiveData als ein anderes LiveData verwaltet wird, übergibt die folgende Implementierung einen leeren Observer an LiveData.observer. Ich war süchtig danach.

Beispielcode

kotlin


//Zählen Sie LiveData, die nur während der Beobachtung jede Sekunde inkrementiert werden
class Counter : LiveData<Int>() {
    private var timer: Timer? = null

    //LiveData, die nur gerade Zahlen liefert und sich dabei auf Zähler der äußeren Klasse stützt
    var oddCounter: MutableLiveData<Int> = object : MutableLiveData<Int>() {
        override fun observe(owner: LifecycleOwner, observer: Observer<Int>) {
            super.observe(owner, observer)

            //Weil es in der äußeren Klasse einen Zähler gibt
            //Wenn Sie dies ebenfalls beobachten, aktivieren Sie es und starten Sie den Zähler.
            [email protected](owner, Observer<Int> { })
        }
    }

    override fun onActive() {
        val task = object : TimerTask() {
            override fun run() {
                var nextCount = (value ?: 0) + 1

                postValue(nextCount)
                if (nextCount % 2 != 0) {
                    oddCounter.postValue(nextCount)
                }
            }
        }
        timer = Timer()
        timer?.scheduleAtFixedRate(task, 0, 1000)
    }

    override fun onInactive() {
        timer?.cancel()
    }
}

class MainKotlinActivity : AppCompatActivity() {
    private val counter = Counter()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //Code, den Aktivitäten und Prozesse in einer Beispielimplementierung beobachten, die von mehreren Eigentümern beobachtet wird
        counter.oddCounter.observe(this, Observer<Int> { value ->
            value?.let { println("activity got $it") }
        })
        counter.oddCounter.observe(ProcessLifecycleOwner.get(), Observer<Int> { value ->
            value?.let { println("process got $it") }
        })
    }
}

Wenn ich das mache, wird der Code "[email protected] (owner, Observer {})" zweimal für verschiedene "owner" -Instanzen aufgerufen, aber zum zweiten Mal "java.lang.IllegalArgumentException" Ich bekomme die Ausnahme: Es kann nicht derselbe Beobachter mit unterschiedlichen Lebenszyklen hinzugefügt werden.

Die Ursache ist wie folgt.

--Observer <Int> {} gibt dieselbe Instanz (innerhalb derselben Klasse) zurück, unabhängig davon, wie oft sie ausgeführt wird

Wahrscheinlich wird mit Observer <Int> {} eine Instanz umgeleitet, da das Schließen nicht vom Umfang des Aufrufers abhängt und behoben ist. Sofern "Observer {}" nicht "Observer {print (owner.toString ())}" ist, hängt die Implementierung von "owner" ab, das sich bei jedem Aufruf ändert. Eine Instanz zurückgegeben.

Erstens dachte ich, dass der Byte-Code zwischen der anonymen inneren Klasse und dem Lambda-Ausdruck völlig unterschiedlich sein würde, also lesen Sie dies Hmmmm.


Dieses Mal habe ich "Observer {}" geschrieben, weil ich eine leere Implementierung der Observer-Instanz erstellen wollte, aber es gibt einen Hintergrund, der passiert ist, als ich der Warnung der IDE gefolgt bin. Tatsächlich kann es auf verschiedene Arten wie folgt geschrieben werden. ..

Der orthodoxste Weg: Javas anonyme innere Klasse.

java


Observer<Integer> nullObserver = new Observer<Integer>() {
    @Override
    public void onChanged(@Nullable Integer integer) {
    }
};
observe(owner, nullObserver);

Dies wird Android Studio jedoch empfohlen, da "durch Lambda ersetzt werden kann", was sich in Code übersetzt, der Lambda-Ausdrücke wie folgt verwendet:

java


Observer<Integer> nullObserver = value -> {};
observe(owner, nullObserver);

Gleiches gilt für Kotlin.

kotlin


val nullObserver = object : Observer<Int> {
    override fun onChanged(t: Int?) {
    }
}
observe(owner, nullObserver);

kotlin


val nullObserver = Observer<Int> { }
observe(owner, nullObserver);

Mir wurde wieder klar, dass ich mir der Unterschiede zwischen ihnen bewusst sein und sie richtig verwenden sollte.

Wenn Sie einfach den ursprünglichen anonymen inneren Klassen- oder Objektausdruck verlassen, kann eine andere Person die gleiche Änderung gemäß dem Vorschlag der IDE erneut vornehmen. Instanziieren Sie daher eine dedizierte Klasse wie folgt: Ich habe es gemacht.

kotlin


private class NullObserver<T> : Observer<T> {
    override fun onChanged(t: T?) {
    }
}

observe(owner, NullObserver<Int>())

Während ich den Qiita-Artikel jetzt kompilierte, fragte ich mich, ob es in Ordnung gewesen wäre, etwas mit Kommentaren zum Quellcode zu ergänzen.

Ende.


Wenn Sie mit dem hier geschriebenen Beispielcode Transformationen verwenden, müssen Sie übrigens keine so komplizierte Himmelsbeobachtung durchführen. Der eigentliche Code kann jedoch etwas komplizierter sein.

Recommended Posts

Java-Lambda-Ausdrücke (Kotlin / JVM) sind nicht immer separate Instanzen
Verstehen Sie Java 8 Lambda-Ausdrücke
Über Java-Lambda-Ausdrücke
Erläutern Sie Java 8-Lambda-Ausdrücke
[Java] Einführung in den Lambda-Ausdruck
[Einführung in Java] Über Lambda-Ausdrücke
Der Ursprung von Java-Lambda-Ausdrücken
Verwendung von Java-Lambda-Ausdrücken
Ich habe versucht, Java-Lambda-Ausdrücke zusammenzufassen
Heutzutage Java Lambda Expressions und Stream API
Organisieren Sie den Unterschied im Schreibkomfort zwischen dem Java-Lambda-Ausdruck und dem Kotlin-Lambda-Ausdruck.