2014-10-13 3 views
8

Скажем, у меня есть переменная activities типа List<Any>?. Если список не пустой и не пустой, я хочу что-то сделать, иначе я хочу сделать что-то еще. Я придумал следующее решение:Идиоматический способ обращения с нулевой или пустой Список в Котлин

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

Есть ли более идиоматический способ сделать это в Котлине?

+1

Примечание: а 'when' с двумя альтернативами очень близко к нормальному' if' –

ответ

11

Для некоторых простых действий, вы можете использовать оператор безопасного вызова, предполагая, что действие также уважает не работает на пустом список (для обработки вашего случая как утративших пусто:

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

для других действий, которые вы можете написать функцию расширения - два варианта в зависимости от того, если вы хотите получить список в качестве this или в качестве параметра:.

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

Что вы можете использовать как:

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

Вы можете также сделать обратную функцию:

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

Я бы избежать сцепления их, потому что тогда вы заменяете if или when о чем-то более многословие. И вы получаете больше в область, которую предоставляют ниже альтернативы, о которых я упоминаю ниже, что является полным ветвлением для ситуаций успеха/неудачи.

Примечание: эти расширения были обобщены на всех потомков Collections, содержащих непустые значения. И работайте больше, чем просто списки.

Альтернатива:

Result библиотека для Котлина дает хороший способ справиться с вашим случаем «сделать это, или что» на основе значений отклика. Для Promises вы можете найти то же самое в библиотеке Kovenant.

Обе эти библиотеки дают вам возможность возвращать альтернативные результаты из одной функции, а также для разветвления кода на основе результатов. Они требуют, чтобы вы контролировали поставщика «ответа», на который воздействовали.

Это хорошие альтернативы Котлин для Optional и Maybe.

Изучение функций расширения Далее (и, возможно, слишком много)

Этот раздел только, чтобы показать, что, когда вы нажмете вопрос, как вопрос, поднятый здесь, вы можете легко найти ответы на многие вопросы в Котлин, чтобы сделать кодируя то, как вы хотите. Если мир не нравится, измените мир. Это не является хорошим или плохим ответом, а скорее дополнительной информацией.

Если вы любите функции расширения и рассмотреть возможность их сцепления в выражении, я бы, вероятно, изменить их следующим образом ...

The withXyz ароматы вернуть this и whenXyz должен возвращать новый тип, позволяющий вся коллекция станет новой (возможно, даже не связанной с оригиналом). В результате в коде, как в следующем:

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

Примечание: полный код для этой версии в конце поста (1)

Но вы могли бы также пойти совершенно новое направление с обычаем " это иначе, что»механизм:

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

там нет никаких ограничений, быть творческим и узнать силу расширений, попробовать новые идеи, и как вы можете видеть, что есть много вариантов, как люди хотят, чтобы закодировать эти типы ситуаций , Stdlib не может поддерживать 8 вариантов этого типа методов, не запутывая. Но каждая группа разработчиков может иметь расширения, соответствующие их стилю кодирования.

Примечание: Полный код для этой версии в конце поста (2)

Пример кода 1:Вот полный код «прикован» версии:

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 
} 

Пример кода 2:Вот полный код для «это иначе что»библиотека (с блоком тестирования):

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

Рассмотрите возможность использования ?.forEach при соответствующем

activities?.forEach { 
    doSmth(it) 
} 

Если вы хотите точно поведение вы описали, я думаю, что ваш вариант читает лучше, чем что-нибудь еще более кратким, я могу думать. (Тем не менее, просто if должно хватить)

+0

Не ясно, что он только хочет, чтобы просматривать список, этот ответ решает только один случай использования «делать что-то "со списком. –

-1

Во-первых, я хотел бы посоветовать сделать функцию расширения в дополнение к @ mlatu отвечают, что не обрабатывает else условию


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

Но использование является не так красиво.

На самом деле вы ищете Может монаду

1

В дополнение к другим ответам, вы можете также использовать оператор безопасного вызова в сочетании с методом расширения isNotEmpty(). Из-за безопасного вызова возвращаемое значение фактически равно Boolean?, которое может быть либо true, false, либо null.Для того, чтобы использовать выражение в пункте if или when, вам нужно проверить, если Явно это true:

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

Альтернативный синтаксис с использованием оператора Элвиса:

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

попробовать это! очень ясно.

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

На самом деле очень элегантный! –

Смежные вопросы