Kotlin 中的 Elvis 运算符:?: 的工作原理并非你想的那样

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Kotlin 中的 Elvis 运算符:不仅仅是"如果为 null,则…"

\n\n

如果你曾经用 Kotlin 编程并遇到过 null,那么你很可能见过这个带有发型的滑稽表情——?: 运算符。它被称为 Elvis 运算符,在 Kotlin 社区中已经成为一个真正的梗。但在这个简单的语法背后,隐藏着意想不到的复杂而有趣的机制。

\n\n

今天,我们将深入探讨 Kotlin 中的 Elvis 运算符,不是将其视为另一种"语法糖",而是作为一个强大的工具,它在字节码层面解决了空安全问题。你将了解当编译器遇到 ?: 时会发生什么,以及为什么在某些情况下它比 if-else 更快。

\n\n

本文基于 Stack Overflow 上一个拥有数千次浏览量的真实问题——我们将其改编为深入的教育内容。开始吧!

\n\n

什么是 Elvis 运算符以及如何使用它

\n\n

从形式上讲,Elvis 运算符是一个二元运算符,如果左操作数不为 null,则返回左操作数,否则返回右操作数。语法如下:

\n\n
val result = nullableValue ?: defaultValue
\n\n

这个名称源于它与猫王埃尔维斯·普雷斯利发型的相似之处,如果你把头侧过来看:?: 就像眼睛和刘海。有趣的是,在编程世界中,这个名字已经流行起来。

\n\n

在 Kotlin 中,这个运算符是 空安全 系统的一部分。它允许你编写简洁的代码,而无需冗长的检查:

\n\n
// 代替这样写:\nval length: Int\nif (str != null) {\n    length = str.length\n} else {\n    length = 0\n}\n\n// 我们这样写:\nval length = str?.length ?: 0
\n\n

教程中未提及的陷阱

\n\n

许多人认为 ?: 只是 if-else 的简写形式。但这并不完全正确。Kotlin 编译器为这两种结构生成 不同的字节码。这种差异可能会影响程序的性能和运行行为。

\n\n

?: 的底层工作原理:字节码和 JVM

\n\n

让我们深入底层。以一个简单的例子为例,看看 Kotlin 编译器为 JVM 将其转换成了什么。

\n\n

Kotlin 源代码

\n\n
fun getLength(str: String?): Int {\n    return str?.length ?: 0\n}
\n\n

反编译后的 Java 代码(大致)

\n\n
public static final int getLength(@Nullable String str) {\n    Integer var10000 = str != null ? str.length() : null;\n    return var10000 != null ? var10000 : 0;\n}
\n\n

看到了吗?编译器不只是将 ?: 替换为 if-else。它创建了一个 Integer? 类型的 临时变量(装箱!),然后应用第二个 ?:。这被称为 双重检查。

\n\n

为什么会这样?

\n\n

因为 str?.length 返回 Int?(可空 Int)。而 ?: 期望左操作数可能为 null。编译器无法将这两个检查"合并"为一个——它必须保留安全调用的语义。

\n\n

结果,如果 str 不为 null,则会执行 两次 空值检查,而不是一次。对于大多数应用程序来说,这并不明显,但在高负载循环中,它可能成为瓶颈。

\n\n

优化:当 Elvis 运算符比 if-else 更快时

\n\n

但好消息是,在某些情况下,Elvis 运算符 生成的字节码比手动检查 更高效。

\n\n

考虑一个带有 提前返回 的例子:

\n\n
// 使用 if-else\nfun process(value: String?): String {\n    if (value == null) return "default"\n    return value.uppercase()\n}\n\n// 使用 Elvis\nfun process(value: String?): String {                

博客

书籍推荐