1. 概述

Kotlin 的空安全(Null-safety)是其核心特性之一,它让我们能更安全、清晰地处理可空值。

本文将深入探讨在 if 语句中如何正确判断可空布尔类型 Boolean?。这看似简单,但在实际开发中稍不注意就会踩坑,尤其是当你从 Java 转向 Kotlin 时。

2. 问题引入

Kotlin 没有像 Java 那样的原始类型(如 boolean),所有类型都是“装箱”的对象类型,比如 Boolean。而 Boolean? 表示一个可以为 truefalsenull 的可空布尔值 —— 这与 Java 的包装类 Boolean 类似。

在 Java 中,我们通常这样判断:

Boolean b = ...;
if (b != null && b) {
   // b 为 true 时执行
} else {
   // b 为 false 或 null 时执行
}

如果直接“翻译”成 Kotlin:

val b: Boolean? = ...
if (b != null && b) {
   // ✅ 正确:Kotlin 会智能转换(smart cast)b 为 Boolean
} else {
   ...
}

上面的代码在 bval(不可变)时完全没问题,因为 Kotlin 编译器能推断出 b != nullb 必然不是 null,从而进行智能类型转换。

⚠️ 但问题来了:如果 bvar(可变变量),智能转换就失效了!

var b: Boolean? = ...
if (b != null && b) { 
    /* ^ 编译错误:
         Smart cast to 'Boolean' is impossible, 
         because 'b' is a mutable property that could have been changed by this time
    */
}

原因很简单:两个条件之间,b 可能被其他线程或代码修改,导致第一次检查非 null,第二次使用时却为 null。

解决方案一:强制解包 ❌

你可能会想到用 b!!

if (b != null && b!!)

虽然能通过编译,但写法啰嗦又危险,一旦 b 为 null 就会抛出 NullPointerException,失去了空安全的意义。

✅ 更推荐的做法是使用值比较,简洁且安全。

3. 使用值比较判断 Boolean?

最直观且推荐的方式是直接与期望值比较:

var b: Boolean? = ...
if (b == true) {
    // 仅当 b 为 true 时执行
} else {
    // b 为 false 或 null 时执行
}

这种写法清晰表达了意图:“只有 true 才进入分支”,避免了 !! 和复杂的逻辑判断。

处理三种独立情况 ✅

如果你需要分别处理 truefalsenull 三种情况,不要写一堆 if-else if,而是使用 Kotlin 的 when 表达式:

var b: Boolean? = null
when (b) {
    true -> println("是 true")
    false -> println("是 false")
    else -> println("是 null") // 注意:else 匹配 null
}

这种方式语义清晰,是 Kotlin 的惯用写法(idiomatic),也更容易维护。

4. 创建 inline 辅助函数提升开发效率

在项目中,我们经常重复类似的判断逻辑。为了减少样板代码,可以封装一些高阶函数。

我们将场景分为两类:

  • If-Then-Do:满足条件时执行一段操作,不关心返回值
  • If-Then-Get:满足条件时执行并获取返回值

4.1. If-Then-Do 函数

适用于只执行副作用(如打印日志、更新 UI)的场景:

inline fun doIfTrue(b: Boolean?, block: () -> Unit) {
    if (b == true) block()
}

inline fun doIfFalse(b: Boolean?, block: () -> Unit) {
    if (b == false) block()
}

inline fun doIfFalseOrNull(b: Boolean?, block: () -> Unit) {
    if (b != true) block() // 等价于 b == false || b == null
}

✅ 关键点:

  • 使用 inline 提升性能,避免 lambda 调用开销
  • block 返回 Unit,表示无返回值
  • doIfFalseOrNullb != true 巧妙覆盖 falsenull

调用方式非常流畅:

var b: Boolean? = getSomeNullableBoolean()
doIfTrue(b) {
    sendNotification()
    updateStatus()
}

4.2. 测试 If-Then-Do 函数

val aList = mutableListOf(0, 1, 2, 3, 4, 5, 6, 7, 8)

doIfTrue(true) { aList[0] = -1 }; assertThat(aList[0]).isEqualTo(-1)
doIfTrue(false) { aList[1] = -1 }; assertThat(aList[1]).isEqualTo(1)
doIfTrue(null) { aList[2] = -1 }; assertThat(aList[2]).isEqualTo(2)

doIfFalse(true) { aList[3] = -1 }; assertThat(aList[3]).isEqualTo(3)
doIfFalse(false) { aList[4] = -1 }; assertThat(aList[4]).isEqualTo(-1)
doIfFalse(null) { aList[5] = -1 }; assertThat(aList[5]).isEqualTo(5)

doIfFalseOrNull(true) { aList[6] = -1 }; assertThat(aList[6]).isEqualTo(6)
doIfFalseOrNull(false) { aList[7] = -1 }; assertThat(aList[7]).isEqualTo(-1)
doIfFalseOrNull(null) { aList[8] = -1 }; assertThat(aList[8]).isEqualTo(-1)

测试覆盖所有边界情况,验证函数行为符合预期。

4.3. If-Then-Get 函数

适用于需要根据条件返回计算结果的场景:

inline fun <R> getIfTrue(b: Boolean?, block: () -> R): R? {
    return if (b == true) block() else null
}

inline fun <R> getIfFalse(b: Boolean?, block: () -> R): R? {
    return if (b == false) block() else null
}

inline fun <R> getIfFalseOrNull(b: Boolean?, block: () -> R): R? {
    return if (b != true) block() else null
}

这些函数在条件成立时执行 block 并返回结果,否则返回 null

你也可以扩展支持默认值:

inline fun <R> getIfTrue(b: Boolean?, orElse: R? = null, noinline block: () -> R?): R? {
    return if (b == true) block() else orElse
}

使用示例:

val result: Int? = getIfTrue(someCondition) {
    val a = calculateA()
    val b = calculateB(a)
    a + b
}

4.4. 测试 If-Then-Get 函数

val aList = listOf("one", "two", "three")
assertThat(getIfTrue(true) { aList[0] }).isEqualTo("one")
assertThat(getIfTrue(false) { aList[0] }).isNull()
assertThat(getIfTrue(null) { aList[0] }).isNull()

assertThat(getIfFalse(true) { aList[1] }).isNull()
assertThat(getIfFalse(false) { aList[1] }).isEqualTo("two")
assertThat(getIfFalse(null) { aList[1] }).isNull()

assertThat(getIfFalseOrNull(true) { aList[2] }).isNull()
assertThat(getIfFalseOrNull(false) { aList[2] }).isEqualTo("three")
assertThat(getIfFalseOrNull(null) { aList[2] }).isEqualTo("three")

测试通过,逻辑正确。

5. 总结

  • ✅ 判断 Boolean? 优先使用 b == true,而非 b != null && b
  • ⚠️ var 类型无法智能转换,不要依赖 b!!
  • ✅ 多分支使用 when 表达式,代码更清晰
  • ✅ 封装 inline 高阶函数可大幅提升代码可读性和复用性,尤其适合通用逻辑(如权限判断、状态机等)

原文中的完整示例代码已整理至 GitHub:https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-4


原始标题:Use of Boolean? in Kotlin if Statement