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.

Da ist das:

fun function(argument: SomeOtherClass) {
    argument.mutableProperty?.let {
        doSomething(it)
        return
    }
    doOtherThing()
}

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

Benutzer-Avatar
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.

mutableProperty?.also { doSomething(it) } ?: doOtherThing()

Ursprüngliche Antwort:

ich würde … benutzen let mit Elvis-Operator.

mutableProperty?.let { doSomething(it) } ?: doOtherThing()

Aus dem Dokument:

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

Benutzer-Avatar
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):

inline fun <T : Any, R> T?.ifNotNullOrElse(ifNotNullPath: (T) -> R, elsePath: () -> R)
        = let { if(it == null) elsePath() else ifNotNullPath(it) }

...

val a: Int? = null
a.ifNotNullOrElse({ println("not null") }, { println("null") })

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 ifelse 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
}

dann kannst du Code wie diesen schreiben:

var nullableVariable :Any? = null
nullableVariable.whenNonNull {
    doSomething(nullableVariable)
}.whenNull {
    doOtherThing()
}

Wie wäre es mit:

argument.mutableProperty
  ?.let { doSomething(it) } 
  ?: doOtherThing()

Normalerweise mache ich nur:

when(val it=argument.mutableProperty) {
    null -> doOtherThing()
    else -> doSomething(it)
}

Benutzer-Avatar
j2emanue

ich schreibe es meistens so:

  takeIf{somecondition}?.also{put somecondition is met code}?:run{put your else code here}

Beachten Sie das Fragezeichen nach takeIf ist ein MUSS. Sie können das Schlüsselwort auch verwenden oder anwenden.

Benutzer-Avatar
aotian16

Dank @zyc zyc verwende ich jetzt diesen Code

inline fun <T> T?.ifNull(block: () -> Unit): T? {
    if (this == null) block()
    return this@ifNull
}

inline fun <T> T?.ifNonNull(block: (T) -> Unit): T? {
    this?.let(block)
    return this@ifNonNull
}
// use
xxxx.ifNull {
    // todo
}.ifNonNull {
    // todo
}

  • Ich mag dieses

    – Makix

    17. August um 12:40 Uhr

1257870cookie-checkKotlin und idiomatische Art zu schreiben, “wenn nicht null, sonst …”, basierend auf veränderlichen Werten

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy