Kotlin 成语:空安全条件?

Kotlin idiom: null-safe conditional?

在Java中,我会这样写:

if (foo != null && foo.bar()) { ...

但是 Kotlin 抱怨说:

Smart cast to 'Foo' is impossible, because 'foo' is a mutable property that could have been changed by this time

认为 它说我写的不是线程安全的。但是,在我使用它的上下文中,我知道它是因为它将始终在单个线程上调用。

foo 需要可变。我意识到将 foo 设为 val 可以解决这个问题,但这在这里是不可能的。

Kotlin 中处理这种情况的正确习惯用法是什么?

诀窍是使用 Kotlin 的优秀 null-safety operators 来避免进行冗余检查。

首先,我们使用安全调用运算符。

foo?.bar()

这是一个 Boolean?(即可为空的布尔值),如果 foo 为空,则为空,否则为 bar() 的结果。现在,Boolean? 显然不是 if 语句中的有效条件,因此我们需要提供 false 的“默认”值。我们使用名字有趣的 Elvis 运算符

来做到这一点
if (foo?.bar() ?: false) { ... }

如果foo为空,则foo?.bar()为空,?: returns右侧的值,即false。如果 foo 是非空的,那么 foo?.bar() 是在 foo 上调用 bar() 的结果,并且(假设结果也是非空的),?: returns现有的非空布尔值。

在这种情况下,空安全调用 returns 布尔值?所以你可以检查它是否等于 true:

if (foo?.bar() == true) {

}

如果条件语句中需要非空 foo,则可以使用常见的 ?.let 惯用语。

foo?.let { foo ->
    if (foo.bar()) {
    
    }
}

如果您知道它只能在同一个线程上访问,则 !! 运算符在 null 检查后是安全的,但是 ?.let 更惯用,所以一旦您习惯了就更容易理解阅读 Kotlin。

如果条件不仅仅是函数调用,例如

foo != null && foo.bar() > 0

您可以使用 letrun:

if (foo.let { it != null && it.bar() > 0 }) { ... }

if (foo.run { this != null && bar() > 0 }) { ... }