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
  • 如果 namenull,整个表达式结果就是 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


原始标题:Kotlin ?.let() vs if not null