编程Android 开发者

安全调用操作符 '?.' 在 Kotlin 中是如何工作的,何时应用于可空类型?

用 Hintsage AI 助手通过面试

答案。

安全调用操作符 '?.' 是 Kotlin 中应对 null 值的关键工具之一,作为一种提高安全性和便利性以处理可能缺失值的语言理念的发展而出现。

问题的背景: 在 Java 中,null 值常常是错误(NullPointerException)的来源。Kotlin 引入了严格的类型系统,明确区分可空和非可空变量,并伴随引入了一些便利工具以方便它们的使用。

问题: 如何在处理可能为 null 的对象时避免 NullPointerException,同时不牺牲代码的可读性和简洁性?

解决方案: 操作符 '?.' 允许顺畅地访问可空变量的属性和方法:如果对象不为 null,则执行调用;如果为 null,则返回 null(或根据需要进行后续逻辑)。

代码示例:

val user: User? = getUser() val name = user?.name // 如果 user != null,将返回 user.name,否则返回 null user?.printInfo() // 如果 user != null,将调用 printInfo()

关键特点:

  • 避免显式的 if (object != null) { ... } 检查。
  • 是嵌套检查链的紧凑而方便的替代方案。
  • 可以与 let 和 Elvis 操作符结合使用,处理 null 的情况。

诱导性问题。

安全调用操作符 '?.' 可以在赋值的左侧使用吗?

不可以。操作符 '?.' 不能用于安全赋值属性或变量;它仅用于访问或调用。以下是不正确的:

user?.name = "Alex" // 编译错误

可以通过显式检查安全地赋值属性:

user?.let { it.name = "Alex" }

当安全调用链第一次遇到 null 时会返回什么?

一旦链中的一个元素 '?.' 等于 null,表达式的结果将变为 null,后续的安全调用不会被执行。

示例:

user?.address?.zipCode // 如果 user 或 address == null,结果为 null

可以将安全调用 '?.' 与返回 Unit 的函数一起使用吗?

可以,调用仍将发生,只要对象不为 null;如果对象为 null,则返回 null 而不是 Unit。

示例:

user?.clearData() // 如果 user == null,什么也不会发生

常见错误和反模式

  • 在 '?.' 后使用 '!!' — 会失去安全调用的意义,可能导致 NullPointerException。
  • 构建长链 '?.' 而不处理输出结果的 null 情况 — 结果可能在后续处理中不易使用,需要额外检查。

生活中的例子

消极案例

开发者在长链安全调用后使用 '!!' 可能导致对象为 null:

val country = user?.address?.city?.country!!

优点:

  • 看起来简洁。

缺点:

  • 链中的任何 null 将导致应用程序抛出 NullPointerException。

积极案例

安全调用与 Elvis 操作符结合使用,以返回默认值:

val country = user?.address?.city?.country ?: "Unknown"

优点:

  • 防止错误,行为可预测,增强可读性。

缺点:

  • 可能难以诊断在链的哪个阶段发生了 null,除非有额外的日志记录。