在日常开发中,空指针异常(NullPointerException)是让人头疼的老问题。尤其是在处理接口返回、用户输入或复杂对象嵌套时,一不小心就崩了。Kotlin 的空安全机制正是为了解决这类问题而生,用起来顺手,还能大大提升代码健壮性。
可空类型与非空类型的区分
Kotlin 默认所有类型都是非空的,比如 String 就不能存 null。如果想让它能为空,得明确声明为 String?:
var name: String = "Tom" // 不能为 null
var nickName: String? = null // 可以为 null
这样编译器会在你试图直接调用可空对象方法时报错,逼你先做判空处理,从源头减少崩溃可能。
安全调用操作符 ?. 的实用场景
当拿到一个可能为空的对象时,用 ?. 能避免直接调用导致崩溃。比如解析用户信息:
val user: User? = getUserFromApi()
val displayName = user?.name?.trim()
这段代码里,只要 user 或 name 任意一个是 null,displayName 就会是 null,不会抛异常。适合链式调用,简洁又安全。
Elvis 操作符 ?: 快速兜底
经常需要给空值设个默认值,比如显示用户名,没有就用“游客”代替:
val displayName = user?.name ?: "游客"
println(displayName)
这种写法比 if-else 简洁多了,特别适合配置项、缓存读取这类场景。
强制调用 !! 要慎用
有些人图省事,直接用 !! 告诉编译器“我确定不为空”:
val len = user!!.name!!.length
但一旦判断失误,程序照样崩溃,而且错误堆栈还不太友好。除非你百分百确认上下文安全,否则别这么干。
let 函数配合安全调用更优雅
有时候要在非空时执行多条语句,用 let 配合 ?. 很合适:
user?.let { u ->
println("用户名:${u.name}")
println("邮箱:${u.email}")
}
只有 user 不为空时,才会进入代码块。逻辑清晰,还能自动推断类型,不用再手动判空拆箱。
函数参数也别忘了空安全
写工具函数时,参数是否可空要明确:
fun formatPhone(phone: String?): String {
return phone?.replace(Regex("\\D"), "") ?: ""
}
这个函数接受可能为空的电话号码,内部处理好空值情况,对外返回总是安全的字符串,调用方也不用再担心。
空安全不是炫技,而是写代码时的一种习惯。刚开始可能觉得啰嗦,但时间久了你会发现,那些曾经频繁出现的崩溃,慢慢消失了。