编程Android开发者

在Kotlin中,Elvis运算符(?:)是什么,如何使用以及为什么要使用它?在应用过程中可能会出现哪些nuance和陷阱?

用 Hintsage AI 助手通过面试

答案。

问题背景:

Elvis运算符(?:)是Kotlin中处理可空值的简单且安全的方式。由于从侧面看这个运算符与猫王埃尔维斯·普雷斯利的发型相似,因此得名。其目的是消除模板代码,例如if (a != null) a else b,并使日常开发更方便。

问题:

直接访问可能为null的值会导致运行时错误(NullPointerException)。因此,需要一种工具,可以优雅地替代为默认值,当变量为null时。

解决方案:

Elvis运算符适用于可空类型。如果运算符左侧的表达式不为null,则返回它;否则,返回右侧的表达式。这使得代码更加简洁和安全。

代码示例:

fun getLength(str: String?): Int { return str?.length ?: 0 } val result = getLength(null) // result == 0 val result2 = getLength("Hello") // result2 == 5

主要特点:

  • 可以避免显式的null检查
  • 可用于抛出异常
  • 与可空类型、表达式和具有副作用的函数完美结合

有陷阱的问题。

如果在Elvis运算符右侧的表达式有副作用,会发生什么?它是否总是执行?

不会!右侧的表达式仅在左侧为null时计算。如果函数有副作用或"昂贵"计算,这可能会很重要。

代码示例:

var called = false val x: String? = "test" val y = x ?: run { called = true; "default" } // called会等于false,因为run根本不会执行

可以使用Elvis运算符抛出异常吗?

可以,在Kotlin中可以这样写:

fun getLengthStrict(str: String?): Int = str?.length ?: throw IllegalArgumentException("str is null")

如果str为null,将会抛出异常。这个机制很有效地对数据进行验证。

可以"链接"多个Elvis运算符吗?

可以,这是一种常见的回退方法:

val name = fromDatabase ?: fromCache ?: "Unknown"

这个表达式将返回第一个非null值或字符串"Unknown"。

常见错误和反模式

  • 复杂的长链Elvis运算符,难以阅读代码
  • 忽视右侧表达式的副作用,这可能导致意外行为
  • 使用Elvis时需要显式检查值是否为null并以不同方式处理

生活中的例子

负面案例

通过Elvis进行多级嵌套null检查:

val title = a?.b?.c?.d?.e ?: defaultTitle

优点:

  • 简洁于简单逻辑

缺点:

  • 难以阅读
  • 当问题不明确时难以调试

正面案例

逐步明确拆解,明确变量名称:

val deepValue = a?.b val deeperValue = deepValue?.c?.d ?: default

优点:

  • 可读性提高
  • 更容易添加额外的检查或日志

缺点:

  • 稍微更冗长;对新手来说可能显得是"多余"的步骤