2014-10-13 14 views
8

Powiedz, że mam zmienną activities typu List<Any>?. Jeśli lista nie jest pusta i nie jest pusta, chcę coś zrobić, w przeciwnym razie chcę zrobić coś innego. Wymyśliłem następujące rozwiązanie:Idiomatyczny sposób obsługi pustej listy lub pustej w Kotlin

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

Czy jest to bardziej idiomatyczny sposób, aby to zrobić w Kotlin?

+1

UWAGA: 'when' z dwóch alternatyw jest bardzo zbliżony do normalnego' if' –

Odpowiedz

11

Dla niektórych prostych czynności można skorzystać z sejfu operator call, zakładając działanie poszanowaniem zasady nie działa na pustej listy (do obsługi przypadek zarówno traci puste:

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

przypadku innych działań można napisać funkcję przedłużacza - dwa warianty w zależności czy chcesz otrzymać listę jako this lub jako parametr.

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

które można wykorzystać jako:

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

Można również zrobić odwrotną funkcję:

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

Wolałbym uniknąć łączenia nich, bo wtedy wymieniasz if lub when oświadczenie z czymś bardziej rozwlekły. A ty wchodzisz w sferę, o której wspomniałem w poniższych alternatywach, czyli pełne rozgałęzienia dla sytuacji sukcesu/porażki.

Uwaga: rozszerzenia zostały uogólnione na wszystkich potomków Collections z wartościami niepustymi. Pracuj więcej niż tylko Listy.

Alternatywy:

Result biblioteki Kotlin daje przyjemny sposób obsłużyć swój przypadek „to zrobić, albo, że” na podstawie wartości odpowiedzi. W przypadku obietnic można znaleźć to samo w bibliotece Kovenant.

Obie te biblioteki dają sposób zwracania alternatywnych wyników z pojedynczej funkcji, a także rozgałęzienia kodu na podstawie wyników. Wymagają one, aby kontrolować dostawcę "odpowiedzi", na którą działa.

To są dobre alternatywy dla Kotlin dla Optional i Maybe.

Odkrywając Funkcje rozszerzeń Dalsze (a może zbyt dużo)

Ta sekcja jest po prostu pokazać, że kiedy trafisz problem podobny do kwestii podniesionej tutaj, można łatwo znaleźć wiele odpowiedzi w Kotlin, aby kodowanie tak, jak chcesz. Jeśli świat nie jest sympatyczny, zmień świat. Nie jest pomyślana jako dobra lub zła odpowiedź, ale raczej jako dodatkowa informacja.

Jeśli lubisz funkcji rozszerzających i rozważyć łączenia ich w wyrażeniu, to pewnie je zmienić w następujący sposób ...

The withXyz smaki wrócić this i whenXyz powinien powrócić nowy typ pozwalający cała kolekcja stanie się nowa (może nawet niezwiązana z oryginałem). Powstały w kodzie jak poniżej:

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

Uwaga: pełny kod dla tej wersji znajduje się na końcu tego postu (1)

Ale można też pójść zupełnie nowy kierunek w zwyczaju " to inaczej, że”mechanizm:

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

nie ma żadnych ograniczeń, być kreatywnym i nauczyć moc rozszerzeń, spróbować nowych pomysłów, a jak widać istnieje wiele odmian tego, jak ludzie chcą, aby zakodować tego typu sytuacjach . Stdlib nie obsługuje 8 odmian tego typu metod, nie wprowadzając zamieszania. Ale każda grupa programistów może mieć rozszerzenia pasujące do ich stylu kodowania.

Uwaga: pełny kod dla tej wersji znajduje się na końcu tego postu (2)

Przykładowy kod 1:Oto pełny kod dla „przykuty” wersji:

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 
} 

Przykładowy kod 2:Oto pełny kod dla „to inaczej że”biblioteki (z badanej jednostki):

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

Rozważ użycie ?.forEach jeśli odpowiedni

activities?.forEach { 
    doSmth(it) 
} 

Jeśli chcesz dokładnie zachowanie opisałeś myślę, że wariant brzmi lepiej niż cokolwiek innego, bardziej zwięzły mogę myśleć. (Jeszcze prosty if powinno wystarczyć)

+0

Nie jest jasne, że on tylko chce iteracyjne listę, to odpowiedź rozwiązuje tylko jeden przypadek użycia „robi coś "z listą. –

-1

Po pierwsze chciałem doradzić, aby funkcję przedłużacza w uzupełnieniu do użytkownika @ mlatu odpowiedź, która obsługuje else warunek


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

Ale użycie nie jest tak piękny.

Faktycznie szukasz dla Może monada

1

W uzupełnieniu do innych odpowiedzi, można również użyć operatora bezpiecznego połączenia w połączeniu z metodą wydłużania isNotEmpty(). Ze względu na bezpieczne połączenie wartość zwracana to faktycznie Boolean?, która może być true, false lub null.Aby użyć wyrażenia w if lub when klauzuli, musisz explictly sprawdzić, czy to true:

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

składnia alternatywna pomocą operatora elvis:

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

spróbuj tego! bardzo czyste.

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

Właściwie bardzo elegancki! –

Powiązane problemy