Android Academy · 课时

避免 NullPointerExceptions

编写安全代码的模式。

第 4 / 4 课13 个步骤

避免 NullPointerExceptions 是 CoddyKit 上的免费 Android Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 4 节课。

目标:不再出现 NPE

当代码使用了实际为 null 的内容时,就会发生 NullPointerException,也就是 NPE。Kotlin 的设计目标是让这类问题变得很少见。

本课将综合您学过的工具,并说明 NPE 仍可能出现的少数情况。

!! 运算符

非 null 断言 !! 会告诉编译器相信某个值不为 null。如果判断错误,它就会抛出 NPE。

它会取消安全保护,因此应尽量少用,并且只能在您确定值不为 null 时使用。

fun main() {
    val name: String? = "Ada"
    println(name!!.length)  // works, name is not null
}

!! 可能导致崩溃

如果值确实为 null,!! 会在那一行准确地抛出 NullPointerException。

这是重新引入 Kotlin 正在防止的问题的主要方式。如果安全调用或 Elvis 运算符可以满足需求,请避免使用 !!。

val name: String? = null
println(name!!.length)
// throws KotlinNullPointerException

优先选择安全替代方案

几乎所有使用 !! 的地方都可以改写为 ?. 和 ?:。安全写法不会导致崩溃。

请比较下面有风险的写法和安全写法。

// Risky:
val len1 = name!!.length
// Safe:
val len2 = name?.length ?: 0

尽早验证

在代码边界处处理 null,然后在内部使用非 null 值。使用 Elvis 运算符结合 return 或 throw,可以在一开始就拒绝缺失的输入。

完成检查后,该值会被智能转换为非 null 类型。

fun length(s: String?): Int {
    val text = s ?: return 0
    return text.length
}

requireNotNull

当 null 确实表示程序错误时,requireNotNull 会抛出清晰的错误,并返回非 null 值。

它比 !! 描述性更强,因为您可以添加消息。

fun start(config: String?) {
    val c = requireNotNull(config) { "config missing" }
    println(c.length)
}

来自 Java 的平台类型

来自 Java 的值具有未知的可空性,这类值称为平台类型。Kotlin 无法保证它们不为 null。

使用 Java 库时,请显式声明预期类型,并采取防御性方式处理 null。

// From Java: String maybe = obj.getName();
val name: String? = obj.name  // be safe
println(name?.length ?: 0)

lateinit 陷阱

lateinit var 允许您延迟初始化非 null 属性。但在赋值之前使用它会抛出异常。

只有在确定该值会在任何访问发生之前被设置时,才应使用 lateinit。

lateinit var config: String
fun read() {
    println(config.length)
    // crashes if config was never set
}

用默认值代替 null

很多时候,您可以选择合理的默认值,从而完全避免可空性。空字符串或空列表通常比 null 更好。

可空类型越少,出现 NPE 的机会就越少。

fun main() {
    val tags: List<String> = emptyList()
    println(tags.size)  // 0, never null
}

安全的数据处理流程

此程序读取一个可选值,安全地转换它,并始终生成结果。不使用 !!,也不会崩溃。

这正是实际 Android 代码应采用的风格。

fun main() {
    val input: String? = null
    val result = input?.trim()?.uppercase() ?: "N/A"
    println(result)  // prints: N/A
}

最佳实践

优先使用非 null 类型,在边界处使用 Elvis 运算符处理 null,并使用 ?.let 执行可选操作。只有在确定不可能为 null 的少数情况下,才使用 !!。

遵循这些习惯,NullPointerExceptions 就会成为过去的问题。

val safe = data?.value ?: default
data?.let { handle(it) }

快速检查

测试您对避免 NullPointerExceptions 的理解。

回顾

Kotlin 中的 NPE 通常来自 !!、来自 Java 的平台类型,或尚未初始化的 lateinit 值。优先使用非 null 类型和安全运算符,可以避免这些问题。

使用 ?.、?:、?.let 以及 requireNotNull 等验证方式,让代码不再因错误而崩溃。现在,您已经完成了 Kotlin 空安全的核心内容。

免费开始

用 AI 导师学习 Kotlin — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
36
课程
152

常见问题解答

「避免 NullPointerExceptions」课时是免费的吗?

是的 — 「避免 NullPointerExceptions」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 4 节课。

「避免 NullPointerExceptions」这节课中我会学到什么?

编写安全代码的模式。 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Android Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「避免 NullPointerExceptions」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Android Academy 课中编写并运行代码吗?

能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 可空类型与非空类型
  2. 安全调用与 Elvis
  3. let、also 与作用域函数
  4. 避免 NullPointerExceptions
← 返回 Android Academy