空安全
在 Kotlin 中,可以有 null
值。当某些内容缺失或尚未设置时,Kotlin 会使用 null
值。你已经在 集合 一章中看到了 Kotlin 返回 null
值的一个例子,当时你尝试访问映射中不存在的键值对。尽管以这种方式使用 null
值很有用,但如果你的代码没有准备好处理它们,你可能会遇到问题。
为了帮助防止程序中出现 null
值问题,Kotlin 引入了空安全。空安全在编译时而不是运行时检测 null
值的潜在问题。
空安全是一组功能的组合,它们允许你:
- 明确声明程序中何时允许
null
值。 - 检查
null
值。 - 使用安全调用来访问可能包含
null
值的属性或函数。 - 声明在检测到
null
值时要执行的操作。
可空类型
Kotlin 支持可空类型,这使得声明的类型可以包含 null
值。默认情况下,类型不允许接受 null
值。可空类型通过在类型声明后明确添加 ?
来声明。
例如:
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
}
TIP
length
是 String 类的一个属性,包含字符串中的字符数。
检查 null 值
你可以在条件表达式中检查 null
值的存在。在以下示例中,describeString()
函数有一个 if
语句,用于检查 maybeString
是否非 null
并且其 length
是否大于零:
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
:
fun lengthString(maybeString: String?): Int? = maybeString?.length
fun main() {
val nullString: String? = null
println(lengthString(nullString))
// null
}
TIP
安全调用可以链式使用,这样如果对象的任何属性包含 null
值,则会返回 null
而不会抛出错误。例如:
person.company?.address?.country
安全调用运算符也可用于安全地调用扩展函数或成员函数。在这种情况下,在调用函数之前会执行 null 检查。如果检查检测到 null
值,则跳过调用并返回 null
。
在以下示例中,nullString
为 null
,因此对 .uppercase()
的调用被跳过并返回 null
:
fun main() {
val nullString: String? = null
println(nullString?.uppercase())
// null
}
使用 Elvis 运算符
你可以使用 Elvis 运算符 ?:
提供一个默认值,以便在检测到 null
值时返回。
在 Elvis 运算符的左侧写入要检查 null
值的内容。在 Elvis 运算符的右侧写入在检测到 null
值时应返回的内容。
在以下示例中,nullString
为 null
,因此访问 length
属性的安全调用返回 null
值。结果,Elvis 运算符返回 0
:
fun main() {
val nullString: String? = null
println(nullString?.length ?: 0)
// 0
}
有关 Kotlin 中空安全的更多信息,请参阅 空安全。
练习
练习
你有一个 employeeById
函数,它允许你访问公司的员工数据库。不幸的是,此函数返回 Employee?
类型的值,因此结果可能为 null
。你的目标是编写一个函数,在提供员工 id
时返回其薪水,如果员工在数据库中缺失则返回 0
。
|---|---|
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) })
}
|---|---|
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 的理解提升到新的水平: