在 Kotlin 中,null
是一种特殊的值,它表示变量没有引用任何对象。
null
)的空间占用在 JVM 中,null
本质上不需要占用任何内存空间,因为它表示一个不存在的对象引用。具体来说:
但这些是引用本身的大小,而不是 null
本身的大小。无论引用的对象是否为 null
,引用的内存大小是固定的。
Int
)的空间占用对于 Int
类型的变量,在 JVM 中存储一个整数需要 4 个字节。这个大小是固定的,因为 Int
类型本质上是一个 32 位的整数。
假设声明一个变量 a
并将其设为 null
或 1
:
var a: Int? = null
var b: Int = 1
a
是一个可空类型(Int?
),它可以为 null
或者是一个 Int
值。因为它是一个引用类型,存储的是引用。b
是一个非空类型(Int
),它直接存储整数值 1
。a
= null
:引用本身占用 4 个字节(32 位 JVM)或 8 个字节(64 位 JVM),null
本身不占用额外空间。b
= 1
:直接存储整数值 1
,占用 4 个字节。a = null
的引用占用 4 个字节,而 b = 1
占用 4 个字节。因此,空间占用相同。a = null
的引用占用 8 个字节,而 b = 1
占用 4 个字节。因此,a = null
占用更多的空间。Kotlin 的类型系统区分了可空类型和非空类型。
非空类型:默认情况下,Kotlin 中的所有类型都是非空的。如果声明一个变量类型而没有指定它可以为 null,那么这个变量不能被赋值为 null。
var a: String = "Hello"
// a = null // 编译错误
可空类型:如果想允许一个变量为 null,需要在类型后面加上一个问号 ?
来标记这个类型为可空类型。
var b: String? = "Hello"
b = null // 正确
?.
当在操作可空类型的变量时,直接调用它的方法或属性可能会导致空指针异常。为了安全地访问可空类型的属性或方法,Kotlin 提供了安全调用操作符 ?.
。
val length = b?.length // 如果 b 不为 null,则返回 b.length;否则返回 null
?:
有时候,当一个表达式为 null 时,可能想要提供一个默认值。这可以通过 Elvis 操作符 ?:
实现。
val length = b?.length ?: 0 // 如果 b 不为 null,则返回 b.length;否则返回 0
!!
如果确定一个可空类型的变量在某个特定的时刻一定不为 null,可以使用非空断言操作符 !!
。这会将任何值转换为一个非空类型,如果值为 null,则抛出一个空指针异常。
val length = b!!.length // 如果 b 为 null,则抛出 NullPointerException
使用 !!
操作符时需要非常小心,因为它可能会导致空指针异常。
as?
Kotlin 还提供了安全的类型转换操作符 as?
,它尝试将一个实例转换为指定的类型,如果实例不是目标类型,则返回 null。
val a: Any = "Kotlin"
val s: String? = a as? String // 成功转换
val n: Int? = a as? Int // 转换失败,n 为 null
Kotlin 的标准库中包含了许多处理可空类型的扩展函数,使得在处理包含 null 元素的集合时更加方便。
val listWithNulls: List<String?> = listOf("Kotlin", null, "Java")
val filteredList = listWithNulls.filterNotNull() // 过滤掉 null,得到 ["Kotlin", "Java"]