1. 概述
Kotlin 的设计核心之一是空安全(null safety),旨在从语言层面杜绝 NullPointerException
。在实际开发中,我们经常需要对可空对象进行判空处理,而这时就绕不开一个经典问题:什么时候该用 ?.let()
,什么时候用 if (x != null)
?
这看似是个小细节,但用错了容易造成代码冗余、可读性下降,甚至隐藏潜在逻辑问题。本文将深入对比这两种写法的适用场景,帮你写出更地道的 Kotlin 代码。
2. 使用 ?.let()
进行安全转换
✅ ?.let()
是 Kotlin 中处理可空对象并执行转换操作的推荐方式。它结合了安全调用(?.
)和作用域函数 let
,只有当接收者非空时才会执行闭包内的逻辑,并返回闭包的计算结果。
val name: String? = fetchNameFromDatabase()
val uppercased = name?.let { it.uppercase() }
上面这段代码中:
fetchNameFromDatabase()
可能返回null
- 如果
name
不为null
,则执行it.uppercase()
并赋值给uppercased
- 如果
name
为null
,整个表达式结果就是null
,不会抛异常
⚠️ 注意:这里 it
指代的就是非空的 name
,类型自动推断为 String
,无需手动强转。
这种写法的优势在于:
- ✅ 一行搞定判空 + 转换
- ✅ 避免嵌套
if
判断 - ✅ 函数式风格,链式调用友好
比如你还可以这样写:
userRepository.findById(123)
?.let { it.email }
?.let { validateEmail(it) }
?.also { sendWelcomeEmail(it) }
整条链路都建立在“前一步不为空”的基础上,非常流畅。
3. 使用 if (x != null)
显式判断
另一种常见做法是使用传统的 if
判空语句。这种方式更直观,尤其适合做一些不需要返回值的操作,比如日志打印、事件上报等副作用行为。
val name: String? = fetchNameFromDatabase()
val uppercased = if (name != null) name.uppercase() else null
这段代码逻辑上和 ?.let()
完全等价,但它更偏向命令式编程风格。
再看一个典型场景——执行副作用:
if (user != null) {
analytics.track("UserLoggedIn", user.id)
showWelcomeMessage(user.name)
}
这时候你并不关心返回值,只是想“如果用户存在,就做几件事”。用 if
更清晰自然。
❌ 不推荐写成:
user?.let {
analytics.track("UserLoggedIn", it.id)
showWelcomeMessage(it.name)
}
虽然也能跑通,但 let
本意是用来做“转换并返回值”的,这里却丢弃了返回值(Unit),属于误用场景,会让其他开发者困惑。
4. 对比总结:何时用哪个?
场景 | 推荐方式 | 原因 |
---|---|---|
对可空对象做数据转换、映射、加工后赋值 | ✅ ?.let() |
简洁、函数式、避免重复判空 |
执行无返回值的操作(如打日志、发事件、弹窗) | ✅ if (x != null) |
语义清晰,符合直觉 |
多个条件混合判断(不只是判空) | ✅ if 系列结构 |
let 无法胜任复杂逻辑 |
需要同时访问多个可空变量 | ✅ if (a != null && b != null) |
可配合解构或 run 使用 |
小技巧:多变量安全操作
当你需要同时确保多个变量非空时,可以用 if
搭配 run
:
if (firstName != null && lastName != null) {
run {
val fullName = "$firstName $lastName"
saveToProfile(fullName)
}
}
或者更优雅地使用 takeIf
+ let
组合(进阶玩法):
firstName?.takeIf { it.isNotBlank() }
?.let { first ->
lastName?.takeIf { it.isNotBlank() }
?.let { last -> "$first $last" }
}
?.also { saveToProfile(it) }
但这已经有点过度复杂了,**简单场景还是优先 if
**。
5. 结论
?.let()
和 if (x != null)
并不是互斥的工具,而是应对不同场景的利器:
- ✅ 优先使用
?.let()
处理“数据转换”类需求,体现 Kotlin 的简洁与安全 - ✅ 优先使用
if (x != null)
处理“副作用执行”类需求,语义更明确 - ⚠️ 别为了炫技强行把
if
改成let
,那样只会让代码更难维护
Null safety 是 Kotlin 的基石之一,合理利用这些语法糖,不仅能减少崩溃风险,还能提升团队代码的一致性和可读性。掌握这些细节,才能真正写出“像样”的 Kotlin 代码,而不是披着 Kotlin 外衣的 Java 风格代码。
📌 一句话口诀:要返回值 → 用
let
;只做事情 → 用if
。