可空变量和智能转换

Nullable var and smart cast

考虑以下 Kotlin 块。

var nullableInt: Int? = null

if (nullableInt != null) {
    val checkedInt: Int = nullableInt
    print("not-null-branch")
} else {
    print("null-branch")
}

Android Studio 告诉我从 Int?Int 的智能转换是不可能的,因为 nullableInt 是可变的。我知道这可能是多线程代码中的问题。

处理该问题的一种方法是使用 val checkedInt: Int = nullableInt!! 进行显式转换,但如果我要在多线程环境中使用编码,那是不可取的。


关闭重复项

关于这个主题,有几个非常接近的问题。但是,我在找到的任何一个中都没有找到令人满意的答案:

讨论了问题出现的原因,但没有提供如何处理问题的建议

有一个 if-not-null 分支,returns 是一个非空值,所以 ?.let{} :? {} 构造在那里工作。由于我的 not-null-branch returns null,所以两个分支都会 运行.

只关注非空分支和非空分支,因此 ?.let{} 构造似乎是正确的。在那个线程中,他们提供了关于在 if 语句之前获取本地副本的建议,这对我来说也是可行的。不幸的是它不是很优雅,我希望有其他选择。


有没有办法在不获取副本的情况下以空安全的方式处理这个空条件分支?

我知道答案可能是“视情况而定”。如果是这样,请说明并详细说明原因。

使用.let代替?.let

因为 .let 扩展函数是为所有类型定义的,包括可为 null 的类型,您实际上可以在没有 safe-call ?. 运算符的情况下调用它。当你这样做时,lambda 将始终被调用,即使是 null 值。如果接收者可以为空,则 let 块中的参数将为空。

但是,lambda 参数 符合智能转换的条件,因为它不可变。

区别如下:

x.let { it -> /* This always runs. 'it' can be null if 'x' is null */ }
x?.let { it -> /* This only runs if 'x' is not null. 'it' is never null. */ }

将其应用于您的示例,您可以这样写:

var nullableInt: Int? = null

nullableInt.let {
    if (it != null) {
         doSomethingWith(it)
    } else {
         doSomethingElse()
    }
}

你不能 start-cast 属性 因为另一个线程可能正在修改它。没有合乎逻辑的解决方法。该语言已经提供了一种不安全的方法来使用您已经提到的 !!

制作引用的本地副本(手动或使用 withlet 之类的范围函数)是微不足道的,因此您无需担心。

我个人认为局部变量是最干净、最易读的方式。您可以避免使用作用域函数嵌套块。

val myVar = myProp
if (myVar != null) {

} else {

}

我认为范围函数最简洁的方法是使用 with。当您同时处理两个分支时,它比 let 读起来更好。

with(myProp) {
    if (this != null) {

    } else {

    }
}

为了简洁地处理这两个分支,以下工作。对于这种情况,我认为 alsolet 更健壮一点,因为您不会意外地 运行 通过从第一个 lambda 返回 null 来两个分支。但是可读性受到如此简洁的影响。

myProp?.also {
    // not null it
} ?: run {
    // null
}