在Kotlin中处理可空或空列表的习惯方式
假设我有一个List<Any>?
类型的变量activities
List<Any>?
。 如果列表不为空而不是空的,我想做一些事情,否则我想做别的事情。 我想出了以下解决方案:
when { activities != null && !activities.empty -> doSomething else -> doSomethingElse }
Kotlin有没有更习惯的方式来做到这一点?
对于一些简单的操作,您可以使用安全调用操作符,假设该操作也不会在空列表上操作(以处理空和空的情况:
myList?.forEach { ...only iterates if not null and not empty }
对于其他行动。 你可以写一个扩展函数 – 两个变体,取决于你是否想要接收这个列表或者作为一个参数:
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
后代。 而不仅仅是列表。
备择方案:
Kotlin的Result库提供了一个很好的方法来处理基于响应值的“做这个或那个”的情况。 对于Promise ,你可以在Kovenant库中找到同样的东西。
这两个库都为您提供了从单个函数返回替代结果的方式,还可以根据结果分支代码。 他们确实要求你正在控制被执行的“答案”的提供者。
这些都是Optional
和Maybe
Kotlin替代品。
探索扩展功能进一步 (也许太多)
本节仅仅是为了说明当你遇到像这里提出的问题一样的问题时,你可以很容易地在Kotlin中找到很多答案,以便按照你想要的方式进行编码。 如果世界不可爱,那么改变世界。 这并不是一个好的或坏的答案,而是附加的信息。
如果你喜欢扩展函数,并且想要考虑将它们链接到一个表达式中,我可能会按如下方式更改它们:
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: 下面是“this otherwise that”库的完整代码(带有单元测试):
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 }
考虑使用?.forEach
如果适用)
activities?.forEach { doSmth(it) }
如果你确切地想要描述你所描述的行为,那么我认为你的变体读得更好,然后我可以想到更简洁的东西。 ( if
应该足够简单)
除了其他答案之外,还可以将安全调用操作符与扩展方法isNotEmpty()
结合使用。 由于安全调用,返回值实际上是Boolean?
可以是true
, false
或null
。 要在if
或when
子句中使用表达式,您需要明确检查它是否为true
:
when { activities?.isNotEmpty() == true -> doSomething else -> doSomethingElse }
使用elvis操作符的可选语法:
when { activities?.isNotEmpty() ?: false -> doSomething else -> doSomethingElse }
首先,我想建议除了@ mlatu的答案,处理else
条件,使扩展功能
public inline fun Map.forEachElse(operation: (Map.Entry) -> Unit, elseBlock: () -> Unit): Unit { if (!empty) for (element in this) operation(element) else elseBlock() }
但用法并不那么美丽。
其实你正在找一个Maybe monad
- 在RecycleView适配器中实现多个ViewHolder类型
- 'com.android.tools.build:gradle:3.0.0-alpha3'和'com.android.tools.build:gradle:3.0.0-alpha1'有什么区别?