Kotlin und idiomatische Art zu schreiben, “wenn nicht null, sonst …”, basierend auf veränderlichen Werten
Lesezeit: 5 Minuten
Angenommen, wir haben diesen Code:
class QuickExample {
fun function(argument: SomeOtherClass) {
if (argument.mutableProperty != null ) {
doSomething(argument.mutableProperty)
} else {
doOtherThing()
}
}
fun doSomething(argument: Object) {}
fun doOtherThing() {}
}
class SomeOtherClass {
var mutableProperty: Object? = null
}
Anders als in Java, wo Sie sich zur Laufzeit um die Null-Dereferenzierung kümmern könnten, wird dies nicht kompiliert – völlig zu Recht. Na sicher, mutableProperty darf innerhalb des ‘if’ nicht mehr einmal null sein.
Meine Frage ist, wie geht man damit am besten um?
Ein paar Optionen sind offensichtlich. Ohne neue Kotlin-Sprachfunktionen zu verwenden, besteht der einfachste Weg offensichtlich darin, den Wert in einen Methodenbereich zu kopieren, der sich später nicht ändert.
Dies hat den offensichtlichen Nachteil, dass Sie früh zurückkehren oder anderweitig vermeiden müssen, den nachfolgenden Code auszuführen – OK in bestimmten, kleinen Kontexten, aber es riecht danach.
Dann gibt es diese Möglichkeit:
fun function(argument: SomeOtherClass) {
argument.mutableProperty.let {
when {
it != null -> {
doSomething(it)
}
else -> {
doOtherThing()
}
}
}
}
aber obwohl es einen klareren Zweck hat, ist es wohl unhandlicher und ausführlicher als die Java-artige Art, damit umzugehen.
Übersehe ich irgendetwas und gibt es eine bevorzugte Sprache, um dies zu erreichen?
IMO würde ich das wirklich einfach denken with(argument.mutableProperty) { if (this != null) a(this) else b() } ist prägnant genug (oder das Äquivalent mit let.) Normalerweise sollte dies kein großes Problem sein und dies ist immer noch ziemlich kurz.
– Salem
25. August 2017 um 8:22 Uhr
Sieht für mich gut aus, danke! Ich schlage vor, Sie fügen es als Antwort hinzu – zumindest ist es eine andere Möglichkeit, dies zu erreichen.
– Rob Pridham
25. August 2017 um 8:26 Uhr
Bob
Aktualisieren:
Wie von franta in den Kommentaren erwähnt, wenn die Methode doSomething() gibt null zurück, dann wird der Code auf der rechten Seite des elvis-Operators ausgeführt, was für die meisten nicht der gewünschte Fall ist. Aber gleichzeitig ist es in diesem Fall sehr wahrscheinlich, dass die doSomething() Methode wird nur etwas tun und nichts zurückgeben.
Und eine Alternative: Wie Protossor in den Kommentaren erwähnt hat, also kann eher verwendet werden als letWeil also kehrt zurück this Objekt anstelle des Ergebnisses des Funktionsbausteins.
Wenn der Ausdruck links von ?: nicht null ist, gibt der Elvis-Operator ihn zurück, andernfalls gibt er den Ausdruck rechts davon zurück. Beachten Sie, dass der Ausdruck auf der rechten Seite nur ausgewertet wird, wenn die linke Seite null ist.
Für einen Codeblock nach dem Ausdruck auf der rechten Seite:
mutableProperty?.let {
doSomething(it)
} ?: run {
doOtherThing()
doOtherThing()
}
Der Nachteil davon scheint die Beschränkung auf einen einzelnen Methodenaufruf auf dem RHS zu sein, im Gegensatz zu einem Codeblock. Übersehe ich da irgendeinen Trick?
– Rob Pridham
25. August 2017 um 9:58 Uhr
@RobPridham Ich denke, Sie könnten es technisch verwenden run und verwenden Sie dort stattdessen ein Lambda, aber das ist eine Strecke
– Salem
25. August 2017 um 10:10 Uhr
Ja. Wie @1blustone erwähnt hat, run oder let kann verwendet werden. Oder Sie können einfach einen Funktionsblock schreiben und ihn durch Aufrufen aufrufen .invoke() oder setzen () nach dem schließenden Lambda, aber diese Funktion wird nicht eingebunden.
– Bob
25. August 2017 um 10:15 Uhr
Ich verstehe nicht, warum diese Antwort so viele Upvotes hat. Sie schlagen etwas vor und gleich im nächsten Abschnitt beweisen Sie, dass Ihr Vorschlag falsch ist. Lesen Sie das Dokument noch einmal -> Wenn die mutableProperty ist nicht null, in das es springt doSomething(it) und wenn diese Methode zurückkehrt nulldann wird der Elvis-Operator ausgeführt doOtherThing().
– franta kocourek
12. Juli 2018 um 19:09 Uhr
Verdammt. Was für ein verworrenes Durcheinander.
– RichieHH
20. Juni 2019 um 4:03 Uhr
Verkauf
Ich glaube nicht, dass es einen wirklich “kurzen” Weg gibt, um dies zu erreichen, aber Sie können einfach eine Bedingung innerhalb verwenden with oder let:
with(mutableVar) { if (this != null) doSomething(this) else doOtherThing() }
mutableVar.let { if (it != null) doSomething(it) else doOtherThing() }
Tatsächlich ist das „Erfassen“ eines veränderlichen Werts einer der Hauptanwendungsfälle von let.
Dies entspricht Ihrer when Aussage.
Es gibt immer die von Ihnen beschriebene Möglichkeit, sie einer Variablen zuzuweisen:
val immutable = mutableVar
if (immutable != null) {
doSomething(immutable)
} else {
doOtherThing()
}
was immer ein netter Fallback ist, falls es zB zu ausführlich wird.
Es gibt wahrscheinlich nicht wirklich ein sehr Hübsch Weg, dies zu erreichen, weil nur die letzte Das Lambda-Argument darf außerhalb von platziert werden ()also würde die Angabe von zwei nicht wirklich zur Syntax aller anderen Standardfunktionen passen.
Du könnte schreiben Sie eine, wenn es Ihnen nichts ausmacht (oder wenn Sie stattdessen Methodenreferenzen übergeben):
Beachten Sie, dass ich persönlich würde nicht Tun Sie dies, da keines dieser benutzerdefinierten Konstrukte sehr angenehm zu lesen ist. IMO: bleib dran let/run und zurückfallen if–else wenn nötig.
fügen Sie eine benutzerdefinierte Inline-Funktion wie folgt hinzu:
inline fun <T> T?.whenNull(block: T?.() -> Unit): T? {
if (this == null) block()
return this@whenNull
}
inline fun <T> T?.whenNonNull(block: T.() -> Unit): T? {
this?.block()
return this@whenNonNull
}
IMO würde ich das wirklich einfach denken
with(argument.mutableProperty) { if (this != null) a(this) else b() }
ist prägnant genug (oder das Äquivalent mitlet
.) Normalerweise sollte dies kein großes Problem sein und dies ist immer noch ziemlich kurz.– Salem
25. August 2017 um 8:22 Uhr
Sieht für mich gut aus, danke! Ich schlage vor, Sie fügen es als Antwort hinzu – zumindest ist es eine andere Möglichkeit, dies zu erreichen.
– Rob Pridham
25. August 2017 um 8:26 Uhr