1

考虑以下 Kotlin 块。

var nullableInt: Int? = null

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

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

处理该问题的一种方法是使用 进行显式强制转换 val checkedInt: Int = nullableInt!!,但如果我将在多线程环境中使用编码,则不建议这样做。


关闭重复项

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

在 Kotlin 中,处理可空值、引用或转换它们的惯用方法是什么,讨论了问题出现的原因,但没有提供有关如何处理它的建议

Kotlin Avoid smart cast for null check有一个 if-not-null 分支,它返回一个非空值,因此该?.let{} :? {}构造在那里工作。由于我的非空分支返回空值,因此两个分支都会运行。

Kotlin“智能转换是不可能的,因为此时属性可能已经改变”只关注非空分支和非空分支,因此?.let{}构造似乎是正确的。在那个线程中,他们提供了关于在if声明之前获取本地副本的建议,这在我的情况下也是可行的。不幸的是,它不是很优雅,我希望有其他选择。


有什么方法可以在不复制的情况下以空安全的方式处理这个空条件分支?

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

4

2 回答 2

3

使用.let代替?.let

因为.let 扩展函数是为所有类型定义的,包括可为空的类型,所以您实际上可以在没有安全调用?.运算符的情况下调用它。当您这样做时,将始终调用 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()
    }
}
于 2020-08-30T11:35:56.927 回答
2

您不能开始转换属性,因为另一个线程可能正在修改它。没有合乎逻辑的方法。该语言已经提供了一种不安全的方式来处理!!您已经提到的。

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

我个人的观点是,局部变量是最干净、最易读的方法。您可以避免嵌套使用范围函数的块。

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
}
于 2020-08-30T12:57:59.987 回答