1. 概述
Kotlin 的空安全(Null-safety)是其核心特性之一,它让我们能更安全、清晰地处理可空值。
本文将深入探讨在 if
语句中如何正确判断可空布尔类型 Boolean?
。这看似简单,但在实际开发中稍不注意就会踩坑,尤其是当你从 Java 转向 Kotlin 时。
2. 问题引入
Kotlin 没有像 Java 那样的原始类型(如 boolean
),所有类型都是“装箱”的对象类型,比如 Boolean
。而 Boolean?
表示一个可以为 true
、false
或 null
的可空布尔值 —— 这与 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 {
...
}
上面的代码在 b
是 val
(不可变)时完全没问题,因为 Kotlin 编译器能推断出 b != null
后 b
必然不是 null,从而进行智能类型转换。
⚠️ 但问题来了:如果 b
是 var
(可变变量),智能转换就失效了!
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
才进入分支”,避免了 !!
和复杂的逻辑判断。
处理三种独立情况 ✅
如果你需要分别处理 true
、false
、null
三种情况,不要写一堆 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
,表示无返回值doIfFalseOrNull
用b != true
巧妙覆盖false
和null
调用方式非常流畅:
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