3.2.7 空安全
原文链接: https://kotlinlang.org/docs/kotlin-tour-null-safety.html
3.2.7 空安全
在 Kotlin 中,可以存在 null 值。当某个东西缺失或尚未设置时,Kotlin 会使用 null 值。在集合一章中,当你试图用一个映射中不存在的键去访问键值对时,你已经看到了 Kotlin 返回 null 值的例子。虽然以这种方式使用 null 值很有用,但如果你的代码没有准备好处理它们,就可能遇到问题。
为了帮助避免程序中 null 值带来的问题,Kotlin 引入了空安全机制。空安全会在编译期而不是运行期检测 null 值的潜在问题。
空安全是一组特性的组合,让你可以:
- 显式声明程序中何时允许出现
null 值。 - 检查
null 值。 - 对可能包含
null 值的属性或函数使用安全调用。 - 声明检测到
null 值时要执行的动作。
可空类型
Kotlin 支持可空类型,它允许所声明的类型具有 null 值。默认情况下,类型不允许接受 null 值。可空类型通过在类型声明之后显式添加 ? 来声明。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| fun main() {
// neverNull has String type
var neverNull: String = "This can't be null"
// Throws a compiler error
neverNull = null
// nullable has nullable String type
var nullable: String? = "You can keep a null here"
// This is OK
nullable = null
// By default, null values aren't accepted
var inferredNonNull = "The compiler assumes non-nullable"
// Throws a compiler error
inferredNonNull = null
// notNull doesn't accept null values
fun strLength(notNull: String): Int {
return notNull.length
}
println(strLength(neverNull)) // 18
println(strLength(nullable)) // Throws a compiler error
}
|
提示: length 是 String 类的一个属性,它包含字符串中的字符数。
检查 null 值
你可以在条件表达式中检查是否存在 null 值。在下面的示例中,describeString() 函数中有一个 if 语句,它检查 maybeString 是否不为 null,以及它的 length 是否大于零:
1
2
3
4
5
6
7
8
9
10
11
12
13
| fun describeString(maybeString: String?): String {
if (maybeString != null && maybeString.length > 0) {
return "String of length ${maybeString.length}"
} else {
return "Empty or null string"
}
}
fun main() {
val nullString: String? = null
println(describeString(nullString))
// Empty or null string
}
|
使用安全调用
要安全地访问可能包含 null 值的对象属性,请使用安全调用运算符 ?.。如果对象本身或它被访问的某个属性是 null,安全调用运算符就会返回 null。如果你想避免 null 值在代码中触发错误,这会很有用。
在下面的示例中,lengthString() 函数使用安全调用,返回字符串的长度或 null:
1
2
3
4
5
6
7
| fun lengthString(maybeString: String?): Int? = maybeString?.length
fun main() {
val nullString: String? = null
println(lengthString(nullString))
// null
}
|
提示: 安全调用可以串联使用:如果对象的任何属性包含 null 值,就会返回 null,而不会抛出错误。例如:kotlin person.company?.address?.country
安全调用运算符也可以用来安全地调用扩展函数或成员函数。在这种情况下,调用函数之前会先做一次 null 检查。如果检查发现 null 值,就会跳过调用并返回 null。
在下面的示例中,nullString 是 null,因此对 .uppercase() 的调用会被跳过,并返回 null:
1
2
3
4
5
| fun main() {
val nullString: String? = null
println(nullString?.uppercase())
// null
}
|
使用 Elvis 运算符
你可以使用 Elvis 运算符 ?: 提供一个在检测到 null 值时返回的默认值。
把需要检查是否为 null 的表达式写在 Elvis 运算符左侧,把检测到 null 值时应返回的内容写在右侧。
在下面的示例中,nullString 是 null,因此访问 length 属性的安全调用返回 null 值。结果,Elvis 运算符返回 0:
1
2
3
4
5
| fun main() {
val nullString: String? = null
println(nullString?.length ?: 0)
// 0
}
|
关于 Kotlin 中空安全的更多信息,请参阅空安全。
练习
习题
你有一个 employeeById 函数,可以用来访问某家公司的员工数据库。遗憾的是,这个函数返回 Employee? 类型的值,因此结果可能是 null。你的目标是编写一个函数:当提供员工 id 时返回该员工的薪水,如果数据库中不存在该员工则返回 0。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| data class Employee (val name: String, var salary: Int)
fun employeeById(id: Int) = when(id) {
1 -> Employee("Mary", 20)
2 -> null
3 -> Employee("John", 21)
4 -> Employee("Ann", 23)
else -> null
}
fun salaryById(id: Int) = // Write your code here
fun main() {
println((1..5).sumOf { id -> salaryById(id) })
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| data class Employee (val name: String, var salary: Int)
fun employeeById(id: Int) = when(id) {
1 -> Employee("Mary", 20)
2 -> null
3 -> Employee("John", 21)
4 -> Employee("Ann", 23)
else -> null
}
fun salaryById(id: Int) = employeeById(id)?.salary ?: 0
fun main() {
println((1..5).sumOf { id -> salaryById(id) })
}
|
示例解答
接下来做什么?
恭喜!既然你已经完成了入门导览,就用进阶导览把你的 Kotlin 水平提升到下一个层次:
另请参阅
上一步
开始进阶 Kotlin 之旅