非空断言
解决可空类型问题的另一种方法是具有特殊知识,即相关引用不为
null
。
要提出这个断言,使用双感叹号 !!
,称为非空断言。如果这看起来令人震惊,那是正常的:相信某些东西不能为 null
是大多数与 null
相关的程序故障的根源(其他的故障可能来自于没有意识到 null
可能会发生)。
x!!
的意思是“忘记 x
可能是 null
的事实——我保证它不是 null
。” 如果 x
不为 null
,则 x!!
产生 x
,否则它会抛出异常:
// NonNullAssertions/NonNullAssert.kt
import atomictest.*
fun main() {
var x: String? = "abc"
x!! eq "abc"
x = null
capture {
val s: String = x!!
} eq "NullPointerException"
}
定义 val s: String = x!!
告诉 Kotlin 忽略它认为自己了解的 x
的情况,只是将它赋值给 s
,s
是一个非可空引用。幸运的是,有运行时支持,当 x
为 null
时会抛出 NullPointerException
。
通常情况下,您不会单独使用 !!
,而是与 .
解引用一起使用:
// NonNullAssertions/NonNullAssertCall.kt
import atomictest.eq
fun main() {
val s: String? = "abc"
s!!.length eq 3
}
如果您每行只使用一个非空断言调用,当异常提供行号时,更容易找到故障。
安全调用 ?.
是一个单一的运算符,但非空断言调用由两个运算符组成:非空断言(!!
)和解引用(.
)。如您在 NonNullAssert.kt
中看到的,您可以单独使用非空断言。
避免使用非空断言,更喜欢使用安全调用或显式检查。非空断言是为了使 Kotlin 与 Java 进行交互而引入的,以及在 Kotlin 不能智能地确保执行必要的检查时的罕见情况。
如果您在代码中经常为同一操作使用非空断言,最好使用一个带有特定断言的单独函数来描述问题。例如,假设您的程序逻辑要求一个特定的键必须存在于 Map
中,并且如果键缺失,则更喜欢抛出异常而不是无声地不做任何操作。与常规方法(方括号)提取值不同,getValue()
在键缺失时会抛出 NoSuchElementException
:
// NonNullAssertions/ValueFromMap.kt
import atomictest.*
fun main() {
val map = mapOf(1 to "one")
map[1]!!.toUpperCase() eq "ONE"
map.getValue(1).toUpperCase() eq "ONE"
capture {
map[2]!!.toUpperCase()
} eq "NullPointerException"
capture {
map.getValue(2).toUpperCase()
} eq "NoSuchElementException: " +
"Key 2 is missing in the map."
}
抛出特定的 NoSuchElementException
在出现问题时提供了更有用的详细信息。
- -
最佳实践的代码仅使用安全调用和引发详细异常的特殊函数。只有在绝对必要的情况下才使用非空断言。尽管非空断言是为了支持与 Java 代码的交互而包含的,但与 Java 交互的更好方法,您可以在 附录 B:Java 互操作性 中学习。
练习和答案可以在 www.AtomicKotlin.com 找到。