Kotlin和惯用的写法,'if not not null,else ......'基于可变值

Rob*_*ham 26 android idioms kotlin

假设我们有这个代码:

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
}
Run Code Online (Sandbox Code Playgroud)

与Java不同,在运行时可以单独担心空解除引用,这不能编译 - 非常正确.当然,mutableProperty在'if'中可能不再为null.

我的问题是处理这个问题的最佳方法是什么?

有一些选择很明显.在不使用任何新的Kotlin语言功能的情况下,最简单的方法显然是将值复制到随后不会更改的方法范围.

有这个:

fun function(argument: SomeOtherClass) {
    argument.mutableProperty?.let {
        doSomething(it)
        return
    }
    doOtherThing()
}
Run Code Online (Sandbox Code Playgroud)

这有一个明显的缺点,你需要提前返回或以其他方式避免执行后续代码 - 确定在某些小的情况下,但它有一股气味.

然后有这种可能性:

fun function(argument: SomeOtherClass) {
    argument.mutableProperty.let {
        when {
            it != null -> {
                doSomething(it)
            }
            else -> {
                doOtherThing()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然它具有更明确的目的,但可以说它比Java风格的处理方式更加笨拙和冗长.

我错过了什么,是否有一个首选的成语来实现这一目标?

Bob*_*Bob 36

更新:

正如franta在评论中所提到的,如果方法doSomething()返回null,那么将执行elvis运算符的乘坐侧的代码,这对于大多数人来说可能不是所希望的情况.但同时,在这种情况下,该方法很可能doSomething()只会做某事而不返回任何东西.

另一种选择:正如protossor在评论中提到的那样,also可以使用而不是let,因为also返回this对象而不是功能块的结果.

mutableProperty?.also { doSomething(it) } ?: doOtherThing()
Run Code Online (Sandbox Code Playgroud)

原始答案:

我会letElvis运营商一起使用.

mutableProperty?.let { doSomething(it) } ?: doOtherThing()
Run Code Online (Sandbox Code Playgroud)

来自doc:

如果?:左边的表达式不为null,则elvis运算符返回它,否则返回右边的表达式.请注意,仅当左侧为空时才评估右侧表达式.

对于右侧表达式后面的代码块:

   mutableProperty?.let {
            doSomething(it)
        } ?: run {
            doOtherThing()
            doOtherThing()
        }
Run Code Online (Sandbox Code Playgroud)

  • 我不明白为什么这个答案有这么多赞成票。您提出了一些建议,而在接下来的部分中,您将证明您的建议是错误的。再读一遍文档 -> 如果 `mutableProperty` 不为 null,它会跳转到 `doSomething(it)`,如果此方法返回 `null`,则 elvis 运算符执行 `doOtherThing()`。 (3认同)
  • 天哪。多么令人费解的混乱。 (3认同)
  • 这样做的缺点似乎是限制了 RHS 上的单个方法调用,而不是代码块。我错过了这个计数的任何技巧吗? (2认同)

Moi*_*ira 14

我不相信有一种真正的"短"方式来实现它,但是你可以简单地使用条件withlet:

with(mutableVar) { if (this != null) doSomething(this) else doOtherThing() }
mutableVar.let { if (it != null) doSomething(it) else doOtherThing() }
Run Code Online (Sandbox Code Playgroud)

实际上,"捕获"可变值是其中一个主要用例let.

这相当于你的when陈述.

总是有你描述的选项,将它分配给变量:

val immutable = mutableVar

if (immutable != null) {
    doSomething(immutable)
} else {
    doOtherThing()
}
Run Code Online (Sandbox Code Playgroud)

如果事情变得过于冗长,这总是一个很好的后备.

可能没有一种非常好的方法来实现这一点,因为只允许将最后一个 lambda参数放在其外部(),因此指定两个并不真正适合所有其他标准函数的语法.

如果你不介意的话,你可以写一个(或者如果你要传递方法引用):

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") })
Run Code Online (Sandbox Code Playgroud)


bor*_*jab 8

关于什么:

argument.mutableProperty
  ?.let { doSomething(it) } 
  ?: doOtherThing()
Run Code Online (Sandbox Code Playgroud)

  • 这在某些情况下可能有效,但如果 doSomething() 返回一个值,就会出现问题。在这种情况下,如果 doSomething() 返回 null,它还会调用 doOtherThing() (5认同)

Mat*_*nze 7

我通常只是这样做:

when(val it=argument.mutableProperty) {
    null -> doOtherThing()
    else -> doSomething(it)
}
Run Code Online (Sandbox Code Playgroud)


小智 6

添加自定义内联函数如下:

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
}
Run Code Online (Sandbox Code Playgroud)

那么你可以编写这样的代码:

var nullableVariable :Any? = null
nullableVariable.whenNonNull {
    doSomething(nullableVariable)
}.whenNull {
    doOtherThing()
}
Run Code Online (Sandbox Code Playgroud)


aot*_*n16 5

感谢@zyc zyc,现在我使用这个代码

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
}
Run Code Online (Sandbox Code Playgroud)
// use
xxxx.ifNull {
    // todo
}.ifNonNull {
    // todo
}
Run Code Online (Sandbox Code Playgroud)