1. 概述

在软件开发中,输入校验和数据状态检查是构建可靠系统的关键环节。本文将介绍如何使用 Kotlin 标准库中内置的 Preconditions 工具函数进行参数校验与状态断言。

这些函数能显著减少样板代码,让逻辑更清晰。对于有经验的开发者来说,掌握它们不仅能提升代码健壮性,还能避免不少线上踩坑。

2. 前置条件检查函数

Kotlin 在标准库中提供了一组简洁高效的前置条件检查工具函数,位于 kotlin 包下,无需引入额外依赖即可使用。它们通过抛出异常的方式中断非法流程,常用于方法入口处的防御性编程。

2.1 require():用于参数合法性校验

require() 函数用于验证传入参数是否满足业务前提。它接收两个参数:

  • value: Boolean:待验证的布尔表达式
  • lazyMessage: () -> Any:延迟计算的错误消息(lambda 形式)

行为规则:当 valuefalse 时,抛出 IllegalArgumentException,携带 lazyMessage 提供的信息。

⚠️ 使用 lambda 而非直接字符串的好处是:只有在校验失败时才会执行字符串拼接或复杂逻辑,避免不必要的性能开销。

fun printPositiveNumber(num: Int) {
    require(num > 0) { "Number must be positive" }
    println(num)
}

踩坑提醒:不要用 require(condition, "message") 这种写法 —— 第二个参数必须是 lambda!

2.2 check():用于对象内部状态校验

check() 的签名与 require() 完全一致,但语义不同:

require() 针对外部输入(arguments)
check() 针对内部状态(state),比如对象字段、缓存、配置等

其行为是:若条件不成立,则抛出 IllegalStateException

来看一个典型场景 —— 计算平均值前确保列表非空:

class AverageCalculator() {
    private val numbers: MutableList<Int> = mutableListOf()
    
    fun add(number: Int) {
        numbers.add(number)
    }
    
    fun clear() {
        numbers.clear()
    }
    
    fun average(): Double {
        check(numbers.size > 0) { "Must have numbers to be able to perform an average." }
        println("Computing averages of numbers: $numbers")
        return numbers.average()
    }
}

📌 注意:这里不是用户传参,而是类自身维护的状态,因此用 check() 更符合语义规范。

2.3 requireNotNull():校验可空参数非空

当我们接收可能为 null 的参数时,可以用 requireNotNull() 快速断言其非空,并安全解包。

✅ 若值为 null,抛出 IllegalArgumentException
✅ 否则返回原值(自动类型推导为非空类型)

class Person(name: String?, age: Int?) {
    val name: String = requireNotNull(name) { "Name must not be null" }
    val age: Int = requireNotNull(age) { "Age must not be null" }
}

这比手写 if (name == null) throw IllegalArgumentException(...) 简洁得多,且具备类型提升能力。

2.4 checkNotNull():校验内部状态中的非空引用

requireNotNull() 对应,checkNotNull() 用于检查对象内部状态中不应为 null 的字段或临时变量。

✅ 条件:值为 null 时抛出 IllegalStateException
✅ 场景:适用于运行时状态一致性检查

fun processStrings(list: List<String?>): String {
    val nonNullStrings = mutableListOf<String>()
    for (string in list) {
        checkNotNull(string) { "The list must not contain null elements" }
        nonNullStrings.add(string)
    }
    return "Processed strings: $nonNullStrings"
}

📌 小技巧:结合 mapNotNull { checkNotNull(it) } 可实现链式处理。

2.5 error():主动抛出运行时异常

error() 是一个极简的辅助函数,定义如下:

fun error(message: Any): Nothing

其中 Nothing 是 Kotlin 特有的“无返回”类型,表示该函数永远不会正常返回。

✅ 用途:明确表示此处应发生异常,终止流程
✅ 适合场景:未支持的分支、不可达路径、资源缺失等

示例一:除零保护

fun divide(a: Int, b: Int): Int {
    if (b == 0) {
        error("Cannot divide by zero")
    }
    return a / b
}

示例二:JSON 类型匹配兜底

fun processJsonArrayOrObject(json: String): JsonNode {
    val genericJson: JsonNode = jacksonObjectMapper().readTree(json)

    when (genericJson) {
        is ArrayNode -> { /* we have a json list */ }
        is ObjectNode -> { /* we have an object */ }
        else -> error("This function only handles list and object wrappers in Json")
    }

    return genericJson
}

⚠️ 注意:error() 抛的是 IllegalStateException,不适合替代参数校验。如果是用户输入问题,请优先使用 require()

3. 总结

Kotlin 的 Preconditions 工具函数虽小,但在日常开发中极为实用。合理区分 require / check 及其 NotNull 变体,不仅能提升代码可读性,也更符合 Kotlin 的编程范式。

函数名 异常类型 推荐使用场景
require() IllegalArgumentException 参数校验
check() IllegalStateException 内部状态检查
requireNotNull() IllegalArgumentException 参数非空断言
checkNotNull() IllegalStateException 状态相关引用非空检查
error() IllegalStateException 主动中断流程,抛出致命错误

所有示例代码已整理至 GitHub:https://github.com/baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-7


原始标题:Preconditions with Kotlin