可空变量和智能转换
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 属性 因为另一个线程可能正在修改它。没有合乎逻辑的解决方法。该语言已经提供了一种不安全的方法来使用您已经提到的 !!
。
制作引用的本地副本(手动或使用 with
或 let
之类的范围函数)是微不足道的,因此您无需担心。
我个人认为局部变量是最干净、最易读的方式。您可以避免使用作用域函数嵌套块。
val myVar = myProp
if (myVar != null) {
} else {
}
我认为范围函数最简洁的方法是使用 with
。当您同时处理两个分支时,它比 let
读起来更好。
with(myProp) {
if (this != null) {
} else {
}
}
为了简洁地处理这两个分支,以下工作。对于这种情况,我认为 also
比 let
更健壮一点,因为您不会意外地 运行 通过从第一个 lambda 返回 null 来两个分支。但是可读性受到如此简洁的影响。
myProp?.also {
// not null it
} ?: run {
// null
}
考虑以下 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!!
进行显式转换,但如果我要在多线程环境中使用编码,那是不可取的。
关闭重复项
关于这个主题,有几个非常接近的问题。但是,我在找到的任何一个中都没有找到令人满意的答案:
?.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 属性 因为另一个线程可能正在修改它。没有合乎逻辑的解决方法。该语言已经提供了一种不安全的方法来使用您已经提到的 !!
。
制作引用的本地副本(手动或使用 with
或 let
之类的范围函数)是微不足道的,因此您无需担心。
我个人认为局部变量是最干净、最易读的方式。您可以避免使用作用域函数嵌套块。
val myVar = myProp
if (myVar != null) {
} else {
}
我认为范围函数最简洁的方法是使用 with
。当您同时处理两个分支时,它比 let
读起来更好。
with(myProp) {
if (this != null) {
} else {
}
}
为了简洁地处理这两个分支,以下工作。对于这种情况,我认为 also
比 let
更健壮一点,因为您不会意外地 运行 通过从第一个 lambda 返回 null 来两个分支。但是可读性受到如此简洁的影响。
myProp?.also {
// not null it
} ?: run {
// null
}