Skip to content

Kotlin 空值安全

Kotlin 在设计时特别关注了空指针异常(NullPointerException,简称 NPE)的问题,这是许多编程语言中常见的错误类型之一。通过其独特的空值处理逻辑和编译时严格的空值检查,可以有效减少空指针异常的产生

可空类型和非空类型

在 Kotlin 中,所有类型默认都是非空类型,即不允许值为 null(例如,String 类型变量的值不能为 null),否则会导致编译报错,如果想允许值为 null,需要在类型声明后加上 ? 符号,将其显式声明为可空类型

对于可空类型,Kotlin 还会强制进行空值检查,如果一个可空类型的变量在使用前没有进行空值检查,也会导致编译报错

kotlin
// 当值可能为 null 时, 应在类型声明后使用 ? 显式声明其为可空类型
fun parseInt(str: String?): Int? {
    if (str == null) return null
    return str.toIntOrNull()
}

// 可空类型在使用前需要进行空值检查, 否则会导致编译报错
fun main() {
    val x = parseInt(null)
    val y = parseInt("1")

    if (x != null && y != null) {
        println(x * y)
    } else {
        println("Either '$x' or '$y' is not a number")
    }
}

类型自动转换

当一个可空类型的变量通过空值检查后,Kotlin 会自动将该变量视为非空类型,只要该变量不重新赋值,后续的使用中就无需再次进行空值检查

kotlin
fun printLengthA(text: String?) {
    // 下方两处 text.length 中, text 均通过了空值检查并被视为非空类型 String
    if (text == null || text.length == 0) {
        println("Test is empty")
        return
    }

    println(text.length)
}

fun printLengthB(text: String?) {
    // 在该条件分支内, text 被视为非空类型 String
    if (text != null) {
        println(text.length)
    }

    // 脱离了空值检测环境, 此处依旧被视为可空类型 String?
    println("Not sure if text is null")
}

空值运算符

Kotlin 提供了一系列空值运算符,用于简化与空值有关的操作:

  • ?. 安全调用运算符,当对象不为 null 时,执行后续的调用,否则返回 null
  • ?: Elvis 运算符,当对象不为 null 时,返回对象本身,否则返回提供的默认值
  • !! 非空断言运算符,取消空值检查的约束,但可能会导致空指针异常(谨慎使用)
kotlin
// 使用 ?. 安全调用运算符, 遇到空值后不再继续调用, 直接返回 null
println(str?.length)
kotlin
// 使用 ?: Elvis 运算符, 遇到空值后返回提供的默认值
println(str?.length ?: -1)

// 可使用作用域函数 run 处理复杂默认值, 其中的最后一个表达式为 run 的返回值
println(str?.length ?: run {
    println("Str is null")
    -1
})

// 也可在遇到空值后直接抛出异常
println(str?.length ?: throw IllegalArgumentException("Str is null"))
kotlin
// 使用 !! 非空断言运算符, 取消空值检查约束, 但可能会导致空指针异常
println(str!!.length)