Kotlin 可空类型与空安全 #
一、空安全概述 #
Kotlin 的空安全设计是其最重要的特性之一,可以在编译时发现潜在的空指针异常。
1.1 NullPointerException 问题 #
在 Java 中,NullPointerException 是最常见的运行时异常:
java
// Java 代码
String name = null;
int length = name.length(); // 运行时抛出 NullPointerException
1.2 Kotlin 的解决方案 #
Kotlin 区分可空类型和不可空类型,在编译时进行检查:
kotlin
// 不可空类型
val name: String = "Kotlin"
// name = null // 编译错误
// 可空类型
val nullableName: String? = null // 正确
二、可空类型 #
2.1 声明可空类型 #
使用 ? 标记可空类型:
kotlin
val name: String? = null
val age: Int? = null
val list: List<String>? = null
2.2 不可空类型 #
默认类型是不可空的:
kotlin
val name: String = "Kotlin"
val age: Int = 25
// 错误:不能赋值为 null
// name = null
// age = null
2.3 类型兼容性 #
kotlin
// 不可空类型可以赋值给可空类型
val a: String = "Hello"
val b: String? = a // 正确
// 可空类型不能赋值给不可空类型
val c: String? = "Hello"
// val d: String = c // 编译错误
三、安全调用操作符 #
3.1 安全调用 ?. #
使用 ?. 安全地调用可空对象的方法:
kotlin
val name: String? = null
// 安全调用
val length = name?.length // 返回 null,不抛出异常
// 链式调用
val upper = name?.uppercase()?.reversed()
3.2 与普通调用对比 #
kotlin
val name: String? = null
// 普通调用(编译错误)
// val length = name.length
// 安全调用
val length = name?.length // null
// 非空断言(可能抛出异常)
val forcedLength = name!!.length // NullPointerException
3.3 链式安全调用 #
kotlin
class Person(val name: String?, val address: Address?)
class Address(val city: String?)
val person: Person? = null
// 链式安全调用
val city = person?.address?.city // null
四、Elvis 操作符 #
4.1 基本用法 #
使用 ?: 提供默认值:
kotlin
val name: String? = null
// 如果 name 为 null,返回 "Unknown"
val displayName = name ?: "Unknown"
// 等价于
val displayName2 = if (name != null) name else "Unknown"
4.2 与安全调用结合 #
kotlin
val name: String? = null
val length = name?.length ?: 0
val upper = name?.uppercase() ?: "DEFAULT"
4.3 抛出异常 #
kotlin
val name: String? = null
// 如果为 null,抛出异常
val displayName = name ?: throw IllegalArgumentException("Name cannot be null")
// 提前返回
fun process(name: String?) {
val validName = name ?: return
println(validName)
}
五、非空断言 #
5.1 使用 !! #
!! 将可空类型转换为非空类型,如果为 null 则抛出异常:
kotlin
val name: String? = "Kotlin"
val length = name!!.length // 6
val nullName: String? = null
// val error = nullName!!.length // NullPointerException
5.2 谨慎使用 #
!! 应该尽量避免使用,只在确定不为 null 时使用:
kotlin
// 不推荐
val name: String? = getName()
val length = name!!.length // 危险
// 推荐
val name: String? = getName()
val length = name?.length ?: 0 // 安全
六、安全类型转换 #
6.1 安全转换 as? #
kotlin
val obj: Any = "Hello"
// 普通转换(可能抛出 ClassCastException)
// val num: Int = obj as Int // ClassCastException
// 安全转换
val num: Int? = obj as? Int // null
val str: String? = obj as? String // "Hello"
6.2 结合 Elvis 使用 #
kotlin
val obj: Any = "123"
val num = (obj as? Int) ?: 0
七、空安全与集合 #
7.1 可空集合 #
kotlin
val list: List<String>? = null
// 安全调用
val size = list?.size ?: 0
val first = list?.firstOrNull()
7.2 集合中的可空元素 #
kotlin
val list: List<String?> = listOf("A", null, "B", null, "C")
// 过滤非空元素
val nonNullList = list.filterNotNull() // ["A", "B", "C"]
// 安全操作
list.map { it?.length }
list.mapNotNull { it?.length }
7.3 常用函数 #
kotlin
val list: List<String?> = listOf("A", null, "B")
// firstOrNull
list.firstOrNull() // "A"
// lastOrNull
list.lastOrNull() // "B"
// getOrNull
list.getOrNull(5) // null
// find
list.find { it?.length == 1 } // "A"
八、智能类型转换 #
8.1 自动类型转换 #
Kotlin 会自动进行智能类型转换:
kotlin
val name: String? = getName()
if (name != null) {
// name 自动转换为 String 类型
println(name.length) // 不需要 ?.
}
// 类似地
when (name) {
null -> println("Name is null")
else -> println(name.length) // 自动转换
}
8.2 智能转换的条件 #
kotlin
// val 局部变量
val name: String? = getName()
if (name != null) {
println(name.length) // 正确
}
// var 局部变量(需要确保不会被修改)
var name2: String? = getName()
if (name2 != null) {
// 如果 name2 可能被其他线程修改,编译器会警告
println(name2.length)
}
// 类属性(需要是 val 且没有自定义 getter)
class Person(val name: String?) {
fun printName() {
if (name != null) {
println(name.length) // 正确
}
}
}
九、延迟初始化与空安全 #
9.1 lateinit #
kotlin
class MainActivity {
private lateinit var name: String
fun init() {
name = "Kotlin"
}
fun print() {
if (::name.isInitialized) {
println(name)
}
}
}
9.2 lazy #
kotlin
class Service {
val database: Database by lazy {
Database.connect()
}
}
// database 不会是 null
十、最佳实践 #
10.1 避免使用 !! #
kotlin
// 不推荐
val length = name!!.length
// 推荐
val length = name?.length ?: 0
10.2 尽早处理 null #
kotlin
fun process(name: String?) {
// 尽早返回
val validName = name ?: return
// 后续代码可以安全使用 validName
println(validName.length)
}
10.3 使用 require/check #
kotlin
fun process(name: String?) {
requireNotNull(name) { "Name cannot be null" }
// name 自动转换为非空
println(name.length)
}
fun process(value: Int?) {
checkNotNull(value) { "Value cannot be null" }
println(value)
}
10.4 使用可空性注解(与 Java 互操作) #
kotlin
// Java 代码
public class User {
@Nullable
public String getName() { return null; }
@NotNull
public String getId() { return "123"; }
}
// Kotlin 调用
val user = User()
val name: String? = user.name // 识别为可空
val id: String = user.id // 识别为非空
十一、常见模式 #
11.1 默认值模式 #
kotlin
val name = nullableName ?: "Default"
val age = nullableAge ?: 0
11.2 提前返回模式 #
kotlin
fun process(data: String?) {
val validData = data ?: return
// 处理 validData
}
11.3 异常模式 #
kotlin
val name = nullableName
?: throw IllegalArgumentException("Name required")
11.4 let 模式 #
kotlin
nullableName?.let { name ->
println(name.length)
println(name.uppercase())
}
十二、总结 #
空安全操作符总结:
| 操作符 | 名称 | 用途 |
|---|---|---|
? |
可空类型 | 声明可空类型 |
?. |
安全调用 | 安全调用方法/属性 |
?: |
Elvis | 提供 null 时的默认值 |
!! |
非空断言 | 强制转换为非空(危险) |
as? |
安全转换 | 安全类型转换 |
下一步,让我们学习 类型转换!
最后更新:2026-03-27