Kotlin中的Nullable:如何无痛处理空值?完整指南

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

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

开始课程

问号背后隐藏着什么?深入Kotlin的nullable世界

\n\n

你是否曾在Java中编写代码,并在最不合时宜的时刻遭遇NullPointerException?如果是——你知道那有多痛苦。Kotlin的开发者一劳永逸地解决了这个问题,将null safety不仅作为一项特性,更作为语言的一种哲学。但新手常常被一个问题困扰:"In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them?"(这正是Stack Overflow上最热门的问题)。

\n\n

今天我们将全面解析处理nullable的各种惯用方式:从简单的?.到巧妙的let再到神奇的!!。最重要的是——我们将深入底层,了解它们在字节码层面是如何工作的。开始吧!

\n\n

快速入门:什么是nullable?

\n\n

在Kotlin中,类型分为两大阵营:nullable(可以为null)和non-nullable(不能为null)。如果你写var name: String = null——编译器会报错。而var name: String? = null——没问题。问号是一个标记,它告诉编译器:"嘿,我知道这里可能为空,别抱怨了"。

\n\n

但如何操作呢?仅仅通过if检查null——这太乏味了,也不符合Kotlin的风格。让我们看看真正的工具。

\n\n

救急三剑客:?, ?: 和 ?.

\n\n

Kotlin提供了三个支柱,支撑着所有nullable处理。记住它们——它们将为你节省数小时的调试时间。

\n\n

1. 安全调用运算符 (?.)

\n\n

这是你代码中最常见的访客。safe call运算符仅在对象不为null时调用方法或属性。如果对象为null——运算符返回null,不会抛出任何异常。

\n\n
val user: User? = null\nval name: String? = user?.name  // name = null,没有任何错误
\n\n

在底层,它被转换为执行常规null检查和条件调用的字节码。没有魔法,只有安全。

\n\n

2. Elvis运算符 (?:)

\n\n

Elvis是你的朋友,当你需要提供一个默认值时。如果左侧不为null——取左侧的值,否则——取右侧的值。

\n\n
val name: String? = null\nval displayName: String = name ?: "访客"  // "访客"
\n\n

Elvis也可以用于提前退出函数:

\n\n
fun getUserName(user: User?): String {\n    val name = user?.name ?: return "匿名"\n    return name\n}
\n\n

3. 非空断言 (!!)

\n\n

这是一个叛逆的运算符。它说:"我确信这里不为null,如果我错了,我准备去死(意思是,抛出NPE)"。请谨慎使用,只有100%确定时才用。

\n\n
val user: User? = getUserFromDatabase() // 我们确信用户存在\nval name: String = user!!.name
\n\n

在底层,!!被编译为常规检查,当值为null时抛出NullPointerException。没什么复杂的,但很危险。

\n\n

惯用方式:let、run、apply 及其家族

\n\n

现在进入最有趣的部分。Stack Overflow上的问题正是关于这个:如何惯用地转换nullable值。答案是——标准库中的扩展函数。

\n\n

let — nullable转换之王

\n\n

let函数仅在对象不为null时执行代码块。在块内部,对象作为it可用(或者可以重命名)。

\n\n
val user: User? = getUser()\nuser?.let { \n    println("用户名:${it.name}")\n    // 这里的 it 保证不为null\n}
\n\n

这是在值非null时执行操作的惯用方式。如果没有let,你不得不写:

\n\n
if (user != null) {\n    println("用户名:${user.name}")\n}

博客

书籍推荐