1. 开始之前
本 Codelab 教您了解可空性以及 null 安全的重要性。可空性是许多编程语言中常见的概念。它指的是变量能够表示值的缺失。在 Kotlin 中,为了实现 null 安全,可空性得到了特殊处理。
前提条件
- 具备 Kotlin 编程基础知识,包括变量、从变量访问方法和属性,以及
println()和main()函数。 - 熟悉 Kotlin 条件语句,包括
if/else语句和布尔表达式。
学习内容
- 什么是
null。 - 可空类型和非可空类型之间的区别。
null安全是什么、它的重要性以及 Kotlin 如何实现null安全。- 如何使用
?.安全调用运算符和!!非空断言运算符访问可空变量的方法和属性。 - 如何使用
if/else条件语句执行null检查。 - 如何使用
if/else表达式将可空变量转换为非可空类型。 - 如何在使用
if/else表达式或?:Elvis 运算符时,在可空变量为null时提供默认值。
所需物品
- 可访问 Kotlin Playground 的网络浏览器。
2. 使用可空变量
什么是 null?
在第 1 单元中,您学习到声明变量时,需要立即为其赋值。例如,当您声明一个 favoriteActor 变量时,您可能会立即为其赋值字符串值 "Sandra Oh"。
val favoriteActor = "Sandra Oh"

但是,如果您没有最喜欢的演员怎么办?您可能希望为变量赋 "Nobody" 或 "None" 的值。这种方法不好,因为您的程序会将 favoriteActor 变量解释为具有 "Nobody" 或 "None" 的值,而不是完全没有值。在 Kotlin 中,您可以使用 null 来指示没有与该变量关联的值。

要在代码中使用 null,请执行以下步骤:
- 在Kotlin Playground 中,将
main()函数主体中的内容替换为将favoriteActor变量设置为null的代码。
fun main() {
val favoriteActor = null
}
- 使用
println()函数打印favoriteActor变量的值,然后运行该程序。
fun main() {
val favoriteActor = null
println(favoriteActor)
}
输出类似于以下代码段:
null
使用 null 重新赋值变量
之前,您了解到可以使用 var 关键字定义的变量重新赋值为同一类型的不同值。例如,只要新名称是 String 类型,就可以将声明时带有一个名称的 name 变量重新赋值为另一个名称。
var favoriteActor: String = "Sandra Oh"
favoriteActor = "Meryl Streep"
声明变量后,有时您可能希望将变量赋值为 null。例如,在声明了您最喜欢的演员之后,您决定完全不想透露您最喜欢的演员。在这种情况下,将 favoriteActor 变量赋值为 null 就很有用。
了解非可空变量和可空变量
要将 favoriteActor 变量重新赋值为 null,请执行以下步骤:
- 将
val关键字更改为var关键字,然后指定favoriteActor变量的类型为String,并将其赋值为您最喜欢的演员的名称。
fun main() {
var favoriteActor: String = "Sandra Oh"
println(favoriteActor)
}
- 移除
println()函数。
fun main() {
var favoriteActor: String = "Sandra Oh"
}
- 将
favoriteActor变量重新赋值为null,然后运行该程序。
fun main() {
var favoriteActor: String = "Sandra Oh"
favoriteActor = null
}
您会收到此错误消息:

在 Kotlin 中,可空类型和非可空类型之间存在区别:
- 可空类型是指_可以_存储
null的变量。 - 非可空类型是指_不能_存储
null的变量。
只有在您明确允许它存储 null 时,一个类型才是可空的。正如错误消息所示,String 数据类型是一个非可空类型,因此您无法将变量重新赋值为 null。

要在 Kotlin 中声明可空变量,需要在类型末尾添加一个 ? 运算符。例如,String? 类型可以存储字符串或 null,而 String 类型只能存储字符串。要声明可空变量,您需要明确添加可空类型。如果没有可空类型,Kotlin 编译器会推断它是一个非可空类型。
- 将
favoriteActor变量的类型从String数据类型更改为String?数据类型。
fun main() {
var favoriteActor: String? = "Sandra Oh"
favoriteActor = null
}
- 在
null重新赋值之前和之后打印favoriteActor变量,然后运行该程序。
fun main() {
var favoriteActor: String? = "Sandra Oh"
println(favoriteActor)
favoriteActor = null
println(favoriteActor)
}
输出类似于以下代码段:
Sandra Oh null
favoriteActor 变量最初存储一个字符串,然后被重新赋值为 null。
试一试
现在您可以使用可空类型 String? 了,您能否初始化一个变量为 Int 值,然后将其重新赋值为 null?
编写一个可空的 Int 值
- 移除
main()函数中的所有代码。
fun main() {
}
- 创建一个可空
Int类型的number变量,然后将其赋值为10。
fun main() {
var number: Int? = 10
}
- 打印
number变量,然后运行该程序。
fun main() {
var number: Int? = 10
println(number)
}
输出符合预期。
10
- 将
number变量重新赋值为null,以确认该变量是可空的。
fun main() {
var number: Int? = 10
println(number)
number = null
}
- 在程序的最后一行添加另一个
println(number)语句,然后运行它。
fun main() {
var number: Int? = 10
println(number)
number = null
println(number)
}
输出符合预期。
10 null
3. 处理可空变量
之前,您学习了使用 . 运算符访问非可空变量的方法和属性。在本节中,您将学习如何使用它来访问可空变量的方法和属性。
要访问非可空 favoriteActor 变量的属性,请执行以下步骤:
- 移除
main()函数中的所有代码,然后声明一个String类型的favoriteActor变量,并将其赋值为您最喜欢的演员的名称。
fun main() {
var favoriteActor: String = "Sandra Oh"
}
- 使用
length属性打印favoriteActor变量值的字符数,然后运行该程序。
fun main() {
var favoriteActor: String = "Sandra Oh"
println(favoriteActor.length)
}
输出符合预期。
9
favoriteActor 变量的值中有九个字符,包括空格。您最喜欢的演员名称中的字符数可能不同。
访问可空变量的属性
假设您希望将 favoriteActor 变量设置为可空,以便没有最喜欢的演员的人可以将变量赋值为 null。
要访问可空 favoriteActor 变量的属性,请执行以下步骤:
- 将
favoriteActor变量的类型更改为可空类型,然后运行该程序。
fun main() {
var favoriteActor: String? = "Sandra Oh"
println(favoriteActor.length)
}
您会收到此错误消息:

此错误是编译错误。如之前的 Codelab 所述,当 Kotlin 无法编译代码时(由于代码中的语法错误),就会发生编译错误。
Kotlin 有意应用语法规则,以实现 null 安全,这指的是_不会意外调用可能为 null 的变量_的保证。这并不意味着变量不能为 null。它意味着如果访问了变量的成员,则变量不能为 null。
这一点至关重要,因为如果在应用运行时试图访问一个为 null 的变量的成员(称为 null 引用),应用会崩溃,因为 null 变量不包含任何属性或方法。这种类型的崩溃称为运行时错误,错误发生在代码编译并运行之后。
由于 Kotlin 的 null 安全特性,可以防止此类运行时错误,因为 Kotlin 编译器会强制对可空类型进行 null 检查。Null 检查指的是在访问变量并将其视为非可空类型之前,检查变量是否可能为 null 的过程。如果您希望将可空值用作其非可空类型,则需要明确执行 null 检查。您将在本 Codelab 后面的使用 if/else 条件语句一节中学习此内容。
在此示例中,代码在编译时失败,因为不允许直接引用 favoriteActor 变量的 length 属性,因为变量可能为 null。
接下来,您将学习处理可空类型的各种技巧和运算符。
使用 ?. 安全调用运算符
您可以使用 ?. 安全调用运算符访问可空变量的方法或属性。
要使用 ?. 安全调用运算符访问方法或属性,请在变量名称后添加一个 ? 符号,然后使用 . 符号访问方法或属性。
?. 安全调用运算符允许更安全地访问可空变量,因为 Kotlin 编译器会阻止任何对 null 引用的成员访问尝试,并为访问的成员返回 null。
要安全地访问可空 favoriteActor 变量的属性,请执行以下步骤:
- 在
println()语句中,将.运算符替换为?.安全调用运算符。
fun main() {
var favoriteActor: String? = "Sandra Oh"
println(favoriteActor?.length)
}
- 运行此程序,然后验证输出是否符合预期。
9
您最喜欢的演员名称的字符数可能不同。
- 将
favoriteActor变量重新赋值为null,然后运行该程序。
fun main() {
var favoriteActor: String? = null
println(favoriteActor?.length)
}
您会看到此输出:
null
请注意,尽管试图访问 null 变量的 length 属性,程序并未崩溃。安全调用表达式只是返回了 null。
使用 !! 非空断言运算符
您还可以使用 !! 非空断言运算符访问可空变量的方法或属性。

在可空变量之后,需要添加 !! 非空断言运算符,然后跟着 . 运算符,再跟着方法或属性,中间不要有任何空格。
正如名称所示,如果您使用 !! 非空断言,这意味着您断言变量的值不为 null,无论它实际是否为 null。
与 ?. 安全调用运算符不同,如果可空变量确实为 null,使用 !! 非空断言运算符可能会导致抛出 NullPointerException 错误。因此,只有在确定变量始终不为 null 或已设置适当的异常处理时才应使用它。未处理的异常会导致运行时错误。您将在本课程后续单元中学习异常处理。
要使用 !! 非空断言运算符访问 favoriteActor 变量的属性,请执行以下步骤:
- 将
favoriteActor变量重新赋值为您最喜欢的演员的名称,然后在println()语句中将?.安全调用运算符替换为!!非空断言运算符。
fun main() {
var favoriteActor: String? = "Sandra Oh"
println(favoriteActor!!.length)
}
- 运行此程序,然后验证输出是否符合预期。
9
您最喜欢的演员名称的字符数可能不同。
- 将
favoriteActor变量重新赋值为null,然后运行该程序。
fun main() {
var favoriteActor: String? = null
println(favoriteActor!!.length)
}
您收到 NullPointerException 错误。

这个 Kotlin 错误表明您的程序在执行期间崩溃了。因此,除非您确定变量不为 null,否则不建议使用 !! 非空断言运算符。
使用 if/else 条件语句
您可以在 if/else 条件语句中使用 if 分支来执行 null 检查。

要执行 null 检查,您可以使用 != 比较运算符检查可空变量是否不等于 null。
if/else 语句
一个 if/else 语句可以与 null 检查结合使用,如下所示:

当与 if/else 语句结合使用时,null 检查非常有用。
nullableVariable != null表达式的null检查用作if条件。if分支内的 Body 1 假设变量不为null。因此,在此主体中,您可以自由地访问变量的方法或属性,就像它是一个非可空变量一样,无需使用?.安全调用运算符或!!非空断言运算符。else分支内的 Body 2 假设变量为null。因此,在此主体中,您可以添加在变量为null时应运行的语句。else分支是可选的。您可以使用if条件语句仅执行null检查,而无需在null检查失败时提供默认操作。
当有使用可空变量的多行代码时,null 检查与 if 条件结合使用更加方便。相反,?. 安全调用运算符更适合对可空变量进行单次引用。
要为 favoriteActor 变量编写一个带有 null 检查的 if/else 语句,请执行以下步骤:
- 再次将
favoriteActor变量赋值为您最喜欢的演员的名称,然后移除println()语句。
fun main() {
var favoriteActor: String? = "Sandra Oh"
}
- 添加一个带有
favoriteActor != null条件的if分支。
fun main() {
var favoriteActor: String? = "Sandra Oh"
if (favoriteActor != null) {
}
}
- 在
if分支的主体中,添加一个println语句,接受字符串"The number of characters in your favorite actor's name is ${favoriteActor.length}.",然后运行该程序。
fun main() {
var favoriteActor: String? = "Sandra Oh"
if (favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
}
}
输出符合预期。
The number of characters in your favorite actor's name is 9.
您最喜欢的演员名称的字符数可能不同。
请注意,您可以直接使用 . 运算符访问名称的 length 方法,因为您在 if 分支内、进行 null 检查后访问 length 方法。因此,Kotlin 编译器知道 favoriteActor 变量不可能为 null,所以编译器允许直接访问该属性。
- 可选:添加一个
else分支来处理演员名称为null的情况。
fun main() {
var favoriteActor: String? = "Sandra Oh"
if (favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
} else {
}
}
- 在
else分支的主体中,添加一个println语句,接受字符串"You didn't input a name."。
fun main() {
var favoriteActor: String? = "Sandra Oh"
if (favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
} else {
println("You didn't input a name.")
}
}
- 将
favoriteActor变量赋值为null,然后运行该程序。
fun main() {
var favoriteActor: String? = null
if(favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
} else {
println("You didn't input a name.")
}
}
输出符合预期。
You didn't input a name.
if/else 表达式
您还可以将 null 检查与 if/else 表达式结合使用,以将可空变量转换为非可空变量。

将 if/else 表达式赋值给非可空类型:
nullableVariable != nullnull检查用作if条件。if分支内的 Body 1 假设变量不为null。因此,在此主体中,您可以访问变量的方法或属性,就像它是一个非可空变量一样,无需使用?.安全调用运算符或!!非空断言运算符。else分支内的 Body 2 假设变量为null。因此,在此主体中,您可以添加在变量为null时应运行的语句。- 在 Body 1 和 Body 2 的最后一行,您需要使用一个表达式或值,其结果为非可空类型,以便在
null检查通过或失败时分别将其赋值给非可空变量。
要使用 if/else 表达式重写程序,使其仅使用一个 println 语句,请执行以下步骤:
- 将
favoriteActor变量赋值为您最喜欢的演员的名称。
fun main() {
var favoriteActor: String? = "Sandra Oh"
if (favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
} else {
println("You didn't input a name.")
}
}
- 创建一个
lengthOfName变量,然后将其赋值给if/else表达式。
fun main() {
var favoriteActor: String? = "Sandra Oh"
val lengthOfName = if (favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
} else {
println("You didn't input a name.")
}
}
- 移除
if和else分支中的两个println()语句。
fun main() {
var favoriteActor: String? = "Sandra Oh"
val lengthOfName = if (favoriteActor != null) {
} else {
}
}
- 在
if分支的主体中,添加favoriteActor.length表达式。
fun main() {
var favoriteActor: String? = "Sandra Oh"
val lengthOfName = if (favoriteActor != null) {
favoriteActor.length
} else {
}
}
直接使用 . 运算符访问 favoriteActor 变量的 length 属性。
- 在
else分支的主体中,添加一个0值。
fun main() {
var favoriteActor: String? = "Sandra Oh"
val lengthOfName = if (favoriteActor != null) {
favoriteActor.length
} else {
0
}
}
值 0 用作名称为 null 时的默认值。
- 在
main()函数末尾,添加一个println语句,接受字符串"The number of characters in your favorite actor's name is $lengthOfName.",然后运行该程序。
fun main() {
var favoriteActor: String? = "Sandra Oh"
val lengthOfName = if (favoriteActor != null) {
favoriteActor.length
} else {
0
}
println("The number of characters in your favorite actor's name is $lengthOfName.")
}
输出符合预期。
The number of characters in your favorite actor's name is 9.
您使用的名称的字符数可能不同。
使用 ?: Elvis 运算符
?: Elvis 运算符是一个可以与 ?. 安全调用运算符一起使用的运算符。通过 ?: Elvis 运算符,当 ?. 安全调用运算符返回 null 时,您可以添加一个默认值。它类似于 if/else 表达式,但方式更加简洁。
如果变量不为 null,则执行 ?: Elvis 运算符之前的表达式。如果变量为 null,则执行 ?: Elvis 运算符之后的表达式。

要修改您的先前程序以使用 ?: Elvis 运算符,请执行以下步骤:
- 移除
if/else条件语句,然后将lengthOfName变量设置为可空favoriteActor变量,并使用?.安全调用运算符调用其length属性。
fun main() {
var favoriteActor: String? = "Sandra Oh"
val lengthOfName = favoriteActor?.length
println("The number of characters in your favorite actor's name is $lengthOfName.")
}
- 在
length属性之后,添加?:Elvis 运算符,后跟值0,然后运行该程序。
fun main() {
var favoriteActor: String? = "Sandra Oh"
val lengthOfName = favoriteActor?.length ?: 0
println("The number of characters in your favorite actor's name is $lengthOfName.")
}
输出与之前的输出相同。
The number of characters in your favorite actor's name is 9.
4. 总结
恭喜!您了解了可空性以及如何使用各种运算符进行管理。
概要
- 变量可以设置为
null以表示它不持有任何值。 - 非可空变量不能被赋值为
null。 - 可空变量可以被赋值为
null。 - 要访问可空变量的方法或属性,需要使用
?.安全调用运算符或!!非空断言运算符。 - 您可以使用带有
null检查的if/else语句,在非可空上下文访问可空变量。 - 您可以使用
if/else表达式将可空变量转换为非可空类型。 - 您可以使用
if/else表达式或?:Elvis 运算符为可空变量为null时提供默认值。
