Kotlin 可空值
最后修改日期:2025 年 3 月 22 日
Kotlin 的类型系统驯服了空指针异常的野兽,将类型分为可空和不可空两种。本教程深入探讨如何使用安全调用、Elvis 运算符等方法来处理空值,通过实用、真实的例子,确保您的代码不会崩溃。
可空类型
默认情况下,Kotlin 将变量锁定为不可空值—添加一个 ? 即可将 null 作为一个选项解锁。这就像一个安全开关,当 null 可能会潜入时,您可以打开它。
fun main() {
var username: String = "Alice" // Locked: no null allowed
// username = null // Error: Null can't crash this party
var email: String? = "bob@site.com" // Unlocked: null's welcome
email = null // All good here
println(email) // Output: null
}
username 保持不可空,而 email 通过 ? 接受 null。想想用户资料—电子邮件可能是可选的,但用户名是必需的。Kotlin 在编译时强制执行这种区分。
安全调用
安全调用运算符 (?.) 绕过 null,仅在对象存在时才获取属性或方法—否则,它会耸耸肩并返回 null,不会发脾气。
fun main() {
val bio: String? = null
val charCount = bio?.length
println(charCount) // Output: null
}
bio?.length 检查可空的 bio,而不会冒崩溃的风险。想象一个社交媒体应用程序—一些用户会跳过 bio,而 ?. 保持应用程序的正常运行,返回 null 而不是崩溃。
Elvis 运算符
当可空值变为空时,Elvis 运算符 (?:) 迅速出现,提供一个回退值,使您的代码保持在正轨上,并提供一个默认值。
fun main() {
val nickname: String? = null
val displayName = nickname ?: "Guest"
println(displayName) // Output: Guest
}
如果 nickname 为 null,nickname ?: "Guest" 则选择 "Guest"。想象一个论坛—没有昵称的用户会得到一个友好的默认值,这要感谢 Elvis 的巧妙救援。
安全类型转换
安全类型转换运算符 (as?) 尝试进行类型切换而无需戏剧性——如果失败,您将得到 null 而不是 ClassCastException。
fun main() {
val input: Any = "42"
val count: Int? = input as? Int
println(count) // Output: null
}
input as? Int 尝试将字符串转换为整数,安全地转换为 null。想想解析用户输入—数字可能会以文本形式到达,而 as? 使管道保持流畅运行而不会崩溃。
非空断言
非空断言 (!!) 关闭了 null 的大门,将可空类型强制转换为非空类型—但如果 null 溜了进来,就会发生 NullPointerException 爆炸。
fun main() {
val token: String? = null
val length = token!!.length // Boom! NullPointerException
println(length) // Never reached
}
token!!.length 押注 token 不为 null—在这里,它输得很惨。仅当您 100% 确定令牌存在时,才在身份验证系统中使用它;否则,它就是一颗定时炸弹。
可空集合
集合可以包含可空类型,而像 filterNotNull 这样的工具会清除 null,只留下好的东西。
fun main() {
val scores: List = listOf(95, null, 87, null, 91)
val validScores = scores.filterNotNull()
println(validScores) // Output: [95, 87, 91]
}
filterNotNull 从 scores 中清除 null。想象一个测验应用程序—一些答案可能未评分 (null),并且这会清理列表以进行最终统计,无需大惊小怪。
使用可空类型的 Let 函数
let 函数与可空类型一起使用,仅在值存在时运行一个代码块—将其视为一个保镖,只允许非空值进入俱乐部。
fun main() {
val address: String? = "123 Main St"
address?.let {
println("Shipping to: $it") // Output: Shipping to: 123 Main St
}
val noAddress: String? = null
noAddress?.let {
println("Shipping to: $it") // No output
}
}
address?.let 仅处理非空地址。在电子商务结账中,这确保了仅在存在送货详细信息时才处理它们—安全且有选择性。
链式安全调用
安全调用可以链接,像专业人士一样导航嵌套的可空类型,如果任何链接中断,则返回 null。
data class User(val profile: Profile?)
data class Profile(val city: String?)
fun main() {
val user: User? = User(null)
val cityLength = user?.profile?.city?.length
println(cityLength) // Output: null
}
user?.profile?.city?.length 深入挖掘多层,在第一个 null 处退出。在旅行应用程序中,仅当用户和个人资料存在时,才会获取城市名称的长度—安全而流畅。
使用多个回退的空合并
Elvis 运算符可以堆叠回退值,在紧急情况下选择第一个非空值,就像一个备用计划,还有一个备用计划。
fun main() {
val primaryPhone: String? = null
val backupPhone: String? = null
val defaultPhone = "N/A"
val contact = primaryPhone ?: backupPhone ?: defaultPhone
println(contact) // Output: N/A
}
primaryPhone ?: backupPhone ?: defaultPhone 寻找有效的电话,最终结果是 "N/A"。在联系人管理器中,这确保您始终有内容可以显示,无论数据有多么稀疏。
结合 Let 和 Elvis
混合使用 let 和 ?: 可以创建一个紧密的空值处理流程,处理非空值,并为其他值提供回退。
fun main() {
val coupon: String? = null
val discount = coupon?.let { it.toInt() } ?: 0
println("Discount: $discount%") // Output: Discount: 0%
}
如果存在,coupon?.let { it.toInt() } ?: 0 将优惠券代码转换为整数,否则默认为 0。在购物车中,这仅在代码有效时才应用折扣—干净且防崩溃。
处理可空值的最佳实践
- 依靠安全调用: 使用
?.避免空值陷阱—当您可以滑行时,为什么还要冒崩溃的风险呢? - 设置智能默认值: 使用
?:插入回退值,在数据不足时保持您的应用程序正常运行。 - 避免
!!: 除非您绝对确定,否则要避开!!—崩溃可不好玩。 - 释放
let: 运用let来把控空值,仅为 VIP(非空值)运行代码。
来源
本教程揭开了 Kotlin 空安全魔法的面纱,通过实用的角度展示了安全调用和 let 等工具。通过这些技巧,您将躲避空值灾难,并保持您的代码像岩石一样稳定。
作者
列出 所有 Kotlin 教程。