2014-10-13 7 views
8

Angenommen, ich habe eine Variable activities des Typs List<Any>?. Wenn die Liste nicht null und nicht leer ist, möchte ich etwas tun, sonst möchte ich etwas anderes machen. Ich kam mit folgender Lösung:Idiomatische Art der Handhabung Nullable oder leer Liste in Kotlin

when { 
    activities != null && !activities.empty -> doSomething 
    else -> doSomethingElse 
} 

Gibt es einen idiomatischen Weg, dies in Kotlin zu tun?

+1

Hinweis: a 'when' mit zwei Alternativen zu einem normalen sehr nahe' if' –

Antwort

11

Für einige einfache Aktionen Monade, die Aktion unter der Annahme, auch respektiert nicht in Betrieb ist auf eine leere Liste (Ihr Fall von beide null und zu handhaben leer:

myList?.forEach { ...only iterates if not null and not empty } 

Für andere Aktionen, die Sie eine Erweiterungsfunktion schreiben können - zwei Varianten, je nachdem, ob Sie die Liste als this oder als Parameter erhalten.

inline fun <E: Any, T: Collection<E>> T?.withNotNullNorEmpty(func: T.() -> Unit): Unit { 
    if (this != null && this.isNotEmpty()) { 
     with (this) { func() } 
    } 
} 

inline fun <E: Any, T: Collection<E>> T?.whenNotNullNorEmpty(func: (T) -> Unit): Unit { 
    if (this != null && this.isNotEmpty()) { 
     func(this) 
    } 
} 

Welche können Sie verwenden, wie:

fun foo() { 
    val something: List<String>? = makeListOrNot() 
    something.withNotNullNorEmpty { 
     // do anything I want, list is `this` 
    } 

    something.whenNotNullNorEmpty { myList -> 
     // do anything I want, list is `myList` 
    } 
} 

Sie auch inverse Funktion tun können:

inline fun <E: Any, T: Collection<E>> T?.withNullOrEmpty(func:() -> Unit): Unit { 
    if (this == null || this.isEmpty()) { 
     func() 
    } 
} 

ich diese Verkettungs vermeiden würde, weil dann ersetzen Sie eine if oder when Aussage mit etwas mehr wortreich. Und Sie kommen mehr in den Bereich, den die Alternativen, die ich unten erwähne, liefern, die für Erfolg/Misserfolg-Situationen voll verzweigen.

Hinweis: Diese Erweiterungen wurden für alle Nachkommen von Collections mit Nicht-Null-Werten verallgemeinert. Und arbeite für mehr als nur Listen.

Alternativen:

Die Result Bibliothek für Kotlin eine schöne Art und Weise gibt Ihren Fall von „dies tun, oder dass“ zu handhaben, basierend auf Antwortwert. Für Promises finden Sie dasselbe in der Bibliothek Kovenant.

Diese beiden Bibliotheken geben Ihnen die Möglichkeit, alternative Ergebnisse von einer einzelnen Funktion zurückzugeben und den Code basierend auf den Ergebnissen zu verzweigen. Sie erfordern, dass Sie den Anbieter der "Antwort" steuern, die bearbeitet wird.

Dies sind gute Kotlin Alternativen zu Optional und Maybe.

Erforschung der Erweiterungsfunktionen Weitere (und vielleicht zu viel)

Dieser Abschnitt ist nur um zu zeigen, dass, wenn Sie ein Problem betroffen wie die Frage hier aufgeworfenen, Sie leicht viele Antworten in Kotlin finden zu machen so programmieren, wie Sie es wollen. Wenn die Welt nicht sympathisch ist, ändere die Welt. Es ist nicht als gute oder schlechte Antwort gedacht, sondern eher als zusätzliche Information.

Wenn Sie die Erweiterungsfunktionen mögen und prüfen wollen, dass sie in einem Ausdruck verketten, würde ich wahrscheinlich sie wie folgt ändern ...

Die withXyz Aromen this zurückzukehren und die whenXyz sollte eine neue Art Rückkehr erlaubt die ganze Sammlung wird zu einer neuen (vielleicht sogar ohne Bezug zum Original). Resultierende in Code wie folgt aus:

val BAD_PREFIX = "abc" 
fun example(someList: List<String>?) { 
    someList?.filterNot { it.startsWith(BAD_PREFIX) } 
      ?.sorted() 
      .withNotNullNorEmpty { 
       // do something with `this` list and return itself automatically 
      } 
      .whenNotNullNorEmpty { list -> 
       // do something to replace `list` with something new 
       listOf("x","y","z") 
      } 
      .whenNullOrEmpty { 
       // other code returning something new to replace the null or empty list 
       setOf("was","null","but","not","now") 
      } 
} 

Hinweis: Die vollständige Code für diese Version ist am Ende des Pfostens (1)

Aber man konnte auch eine völlig neue Richtung mit einem benutzerdefinierten gehen " dies sonst, dass“Mechanismus:

fun foo(someList: List<String>?) { 
    someList.whenNullOrEmpty { 
     // other code 
    } 
    .otherwise { list -> 
     // do something with `list` 
    } 
} 

keine Grenzen gesetzt sind, kreativ sein und die Kraft der Erweiterungen lernen, neue Ideen auszuprobieren, und wie Sie gibt es viele Variationen sehen, wie die Menschen wollen diese Art von Situationen codieren . Die stdlib kann 8 Variationen dieser Art von Methoden nicht unterstützen, ohne zu verwirren. Jede Entwicklungsgruppe kann jedoch Erweiterungen haben, die ihrem Codierungsstil entsprechen.

Hinweis: Die vollständige Code für diese Version ist am Ende des Pfostens (2)

Beispielcode 1:Hier wird der vollständige Code für die "gekettet" Version ist:

inline fun <E: Any, T: Collection<E>> T?.withNotNullNorEmpty(func: T.() -> Unit): T? { 
    if (this != null && this.isNotEmpty()) { 
     with (this) { func() } 
    } 
    return this 
} 

inline fun <E: Any, T: Collection<E>, R: Any> T?.whenNotNullNorEmpty(func: (T) -> R?): R? { 
    if (this != null && this.isNotEmpty()) { 
     return func(this) 
    } 
    return null 
} 

inline fun <E: Any, T: Collection<E>> T?.withNullOrEmpty(func:() -> Unit): T? { 
    if (this == null || this.isEmpty()) { 
     func() 
    } 
    return this 
} 

inline fun <E: Any, T: Collection<E>, R: Any> T?.whenNullOrEmpty(func:() -> R?): R? { 
    if (this == null || this.isEmpty()) { 
     return func() 
    } 
    return null 
} 

Beispielcode 2:Hier ist der vollständige Code für einen „dies sonst dass“Bibliothek (mit Unit-Test):

inline fun <E : Any, T : Collection<E>> T?.withNotNullNorEmpty(func: T.() -> Unit): Otherwise { 
    return if (this != null && this.isNotEmpty()) { 
     with (this) { func() } 
     OtherwiseIgnore 
    } else { 
     OtherwiseInvoke 
    } 
} 

inline fun <E : Any, T : Collection<E>> T?.whenNotNullNorEmpty(func: (T) -> Unit): Otherwise { 
    return if (this != null && this.isNotEmpty()) { 
     func(this) 
     OtherwiseIgnore 
    } else { 
     OtherwiseInvoke 
    } 
} 

inline fun <E : Any, T : Collection<E>> T?.withNullOrEmpty(func:() -> Unit): OtherwiseWithValue<T> { 
    return if (this == null || this.isEmpty()) { 
     func() 
     OtherwiseWithValueIgnore<T>() 
    } else { 
     OtherwiseWithValueInvoke(this) 
    } 
} 

inline fun <E : Any, T : Collection<E>> T?.whenNullOrEmpty(func:() -> Unit): OtherwiseWhenValue<T> { 
    return if (this == null || this.isEmpty()) { 
     func() 
     OtherwiseWhenValueIgnore<T>() 
    } else { 
     OtherwiseWhenValueInvoke(this) 
    } 
} 

interface Otherwise { 
    fun otherwise(func:() -> Unit): Unit 
} 

object OtherwiseInvoke : Otherwise { 
    override fun otherwise(func:() -> Unit): Unit { 
     func() 
    } 
} 

object OtherwiseIgnore : Otherwise { 
    override fun otherwise(func:() -> Unit): Unit { 
    } 
} 

interface OtherwiseWithValue<T> { 
    fun otherwise(func: T.() -> Unit): Unit 
} 

class OtherwiseWithValueInvoke<T>(val value: T) : OtherwiseWithValue<T> { 
    override fun otherwise(func: T.() -> Unit): Unit { 
     with (value) { func() } 
    } 
} 

class OtherwiseWithValueIgnore<T> : OtherwiseWithValue<T> { 
    override fun otherwise(func: T.() -> Unit): Unit { 
    } 
} 

interface OtherwiseWhenValue<T> { 
    fun otherwise(func: (T) -> Unit): Unit 
} 

class OtherwiseWhenValueInvoke<T>(val value: T) : OtherwiseWhenValue<T> { 
    override fun otherwise(func: (T) -> Unit): Unit { 
     func(value) 
    } 
} 

class OtherwiseWhenValueIgnore<T> : OtherwiseWhenValue<T> { 
    override fun otherwise(func: (T) -> Unit): Unit { 
    } 
} 


class TestBrancher { 
    @Test fun testOne() { 
     // when NOT null or empty 

     emptyList<String>().whenNotNullNorEmpty { list -> 
      fail("should not branch here") 
     }.otherwise { 
      // sucess 
     } 

     nullList<String>().whenNotNullNorEmpty { list -> 
      fail("should not branch here") 
     }.otherwise { 
      // sucess 
     } 

     listOf("a", "b").whenNotNullNorEmpty { list -> 
      assertEquals(listOf("a", "b"), list) 
     }.otherwise { 
      fail("should not branch here") 
     } 

     // when YES null or empty 

     emptyList<String>().whenNullOrEmpty { 
      // sucess 
     }.otherwise { list -> 
      fail("should not branch here") 
     } 

     nullList<String>().whenNullOrEmpty { 
      // success 
     }.otherwise { 
      fail("should not branch here") 
     } 

     listOf("a", "b").whenNullOrEmpty { 
      fail("should not branch here") 
     }.otherwise { list -> 
      assertEquals(listOf("a", "b"), list) 
     } 

     // with NOT null or empty 

     emptyList<String>().withNotNullNorEmpty { 
      fail("should not branch here") 
     }.otherwise { 
      // sucess 
     } 

     nullList<String>().withNotNullNorEmpty { 
      fail("should not branch here") 
     }.otherwise { 
      // sucess 
     } 

     listOf("a", "b").withNotNullNorEmpty { 
      assertEquals(listOf("a", "b"), this) 
     }.otherwise { 
      fail("should not branch here") 
     } 

     // with YES null or empty 

     emptyList<String>().withNullOrEmpty { 
      // sucess 
     }.otherwise { 
      fail("should not branch here") 
     } 

     nullList<String>().withNullOrEmpty { 
      // success 
     }.otherwise { 
      fail("should not branch here") 
     } 

     listOf("a", "b").withNullOrEmpty { 
      fail("should not branch here") 
     }.otherwise { 
      assertEquals(listOf("a", "b"), this) 
     } 


    } 

    fun <T : Any> nullList(): List<T>? = null 
} 
1

Betrachten ?.forEach falls

activities?.forEach { 
    doSmth(it) 
} 

mit der Wenn Sie genau das Verhalten möchten Sie beschrieben Ich denke, Ihre Variante dann etwas besser liest sonst prägnanter ich mir vorstellen kann. (Und doch einfach if sollte ausreichen)

+0

Es ist nicht klar, dass er nur die Liste zu durchlaufen will, löst diese Antwort nur ein Anwendungsfall von „etwas zu tun "mit einer Liste. –

-1

Zum einen I-Erweiterungsfunktion zusätzlich zu @ mlatu Antwort zu machen beraten wollte, die else Zustand Griffe


public inline fun Map.forEachElse(operation: (Map.Entry) -> Unit, elseBlock:() -> Unit): Unit { 
     if (!empty) 
      for (element in this) operation(element) 
     else 
      elseBlock() 
    } 

Aber die Nutzung ist nicht so schön.

für eine Sie suchen Eigentlich Vielleicht können Sie den sicheren Anruf Operator

1

Zusätzlich zu den anderen Antworten, können Sie auch den Safe-Call-Operator in Verbindung mit der Erweiterungsmethode isNotEmpty() verwenden. Wegen des sicheren Aufrufs ist der Rückgabewert tatsächlich , der entweder true, false oder null sein kann.Um die Expression in einer Klausel if oder when zu verwenden, müssen Sie explictly überprüfen, ob es true ist:

when { 
    activities?.isNotEmpty() == true -> doSomething 
    else -> doSomethingElse 
} 

Alternative Syntax des elvis-Operator:

when { 
    activities?.isNotEmpty() ?: false -> doSomething 
    else -> doSomethingElse 
} 
4

dies versuchen! sehr deutlich.

var array: List<String>? = null 
if (array.orEmpty().isEmpty()) { 
    // empty 
} else { 
    // not empty 
} 
+0

Eigentlich sehr elegant! –

Verwandte Themen