Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

非空断言

解决可空类型问题的另一种方法是具有特殊知识,即相关引用不为 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 的情况,只是将它赋值给 ss 是一个非可空引用。幸运的是,有运行时支持,当 xnull 时会抛出 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 找到。