ZetCode

Kotlin 空值关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的空安全系统有助于消除空引用异常。null 关键字表示值的缺失。本教程通过实际例子探讨了 Kotlin 中的空值处理。

基本定义

在 Kotlin 中,类型默认情况下是不可空的。要允许空值,您必须使用 ? 显式声明类型为可空。null 关键字表示一个空引用。Kotlin 提供了几个运算符来安全地处理空值。

可空类型声明

要声明一个可以包含 null 的变量,请在其类型后附加 ?。这使得该类型可空。如果没有 ?,该变量就不能包含 null。

NullableTypes.kt
package com.zetcode

fun main() {

    var name: String = "Kotlin"
    // name = null // Compilation error
    
    var nullableName: String? = "Kotlin"
    nullableName = null // Valid
    
    println(nullableName) // Output: null
}

这里,name 不能为 null,而 nullableName 可以。注释行将导致编译错误。这展示了 Kotlin 在编译时的空安全性。

安全调用运算符 (?.)

安全调用运算符 ?. 允许您安全地访问可空对象的属性或方法。如果对象为 null,则表达式返回 null 而不是抛出异常。

SafeCalls.kt
package com.zetcode

fun main() {

    val str: String? = null
    val length = str?.length
    
    println(length) // Output: null
    
    val str2: String? = "Hello"
    println(str2?.length) // Output: 5
}

第一个安全调用返回 null,因为 str 为 null。第二个调用返回长度,因为 str2 包含一个字符串。这可以防止 NullPointerException。

Elvis 运算符 (?:)

Elvis 运算符 ?: 在可空表达式为 null 时提供默认值。它类似于其他语言中的三元运算符,但专门用于空值检查。

ElvisOperator.kt
package com.zetcode

fun main() {

    val name: String? = null
    val length = name?.length ?: 0
    
    println(length) // Output: 0
    
    val name2: String? = "Kotlin"
    println(name2?.length ?: 0) // Output: 6
}

name 为 null 时,Elvis 运算符返回 0。当 name2 有一个值时,它返回字符串长度。这为 null 值提供了一个安全的后备方案。

非空断言 (!!)

非空断言运算符 !! 将任何值转换为非空类型。如果该值为 null,则抛出 NullPointerException。仅当您确定该值不为 null 时才使用此运算符。

NotNullAssertion.kt
package com.zetcode

fun main() {

    val str: String? = "Kotlin"
    println(str!!.length) // Output: 6
    
    val str2: String? = null
    // println(str2!!.length) // Throws NullPointerException
}

第一个断言成功,因为 str 不为 null。注释行将抛出异常,因为 str2 为 null。此运算符应谨慎使用。

使用 as? 的安全转换

安全转换运算符 as? 尝试转换,如果失败则返回 null。这结合了空安全性和类型转换,防止了 ClassCastException。

SafeCasts.kt
package com.zetcode

fun main() {

    val obj: Any = "Kotlin"
    val str: String? = obj as? String
    val num: Int? = obj as? Int
    
    println(str) // Output: Kotlin
    println(num) // Output: null
}

第一个转换成功,因为 obj 是一个 String。第二个转换失败,但返回 null 而不是抛出异常。这比常规转换更安全。

带有可空的 let 函数

let 函数仅当对象不为 null 时才执行一个块。它对于安全地对可空对象执行操作非常有用。

LetFunction.kt
package com.zetcode

fun main() {

    val name: String? = "Kotlin"
    
    name?.let {
        println("Name length is ${it.length}") // Output: Name length is 6
    }
    
    val nullName: String? = null
    nullName?.let {
        println("This won't be printed")
    }
}

第一个 let 块执行,因为 name 不为 null。第二个块不执行,因为 nullName 为 null。这种模式在 Kotlin 中常用于空安全操作。

延迟初始化属性

lateinit 修饰符允许稍后初始化非空属性。当依赖注入或测试设置提供值时,这很有用。

LateInit.kt
package com.zetcode

class User {
    lateinit var name: String
    
    fun initialize() {
        name = "Kotlin"
    }
    
    fun printName() {
        if (::name.isInitialized) {
            println(name)
        }
    }
}

fun main() {

    val user = User()
    // println(user.name) // Throws UninitializedPropertyAccessException
    
    user.initialize()
    user.printName() // Output: Kotlin
}

name 属性声明时没有 null,但稍后初始化。在初始化之前访问它会抛出异常。isInitialized 检查验证初始化状态。

空安全性的最佳实践

来源

Kotlin 空安全性文档

本教程深入介绍了 Kotlin 的 null 关键字和空安全特性。我们探讨了可空类型、安全调用、Elvis 运算符等等。适当的空值处理使 Kotlin 代码更健壮,更不易出现运行时异常。

作者

我的名字是 Jan Bodnar,我是一位热情的程序员,拥有多年的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我写了超过 1400 篇文章和 8 本电子书。我拥有超过八年的编程教学经验。

列出 所有 Kotlin 教程