Kotlin空检查编译器契约如何工作?自定义applyIf如何透传非空断言?
核心问题原因
你原来的实现中condition是固定Boolean类型的入参,Kotlin编译器无法跟踪这个布尔值对应的判断逻辑(比如source != null)和后续block执行的关联关系,因此空检查的智能转换信息无法穿透函数边界传递到block内部。
优化实现方案
你需要将参数改为函数类型的条件判断,同时结合Kotlin契约保证调用规则,即可让编译器正确识别条件判断的状态,实现预期的智能转换效果。以下提供两种常用重载实现,覆盖不同使用场景:
1. 通用条件判断版本
适用于所有自定义条件判断场景,支持针对接收器自身或外部变量的条件校验:
@OptIn(ExperimentalContracts::class) inline fun <R> R.applyIf(condition: () -> Boolean, block: R.() -> Unit): R { contract { // 保证condition只会被执行一次 callsInPlace(condition, InvocationKind.EXACTLY_ONCE) // 保证block最多只会被执行一次 callsInPlace(block, InvocationKind.AT_MOST_ONCE) } if (condition()) block() return this }
使用示例
.applyIf({ source != null }) { // 此时编译器可以正确识别source已非空,不会触发告警 sourceClassName = source.className sourceMethodName = source.methodName }
2. 非空判断专用版本
针对最常用的非空校验场景做专门优化,使用更简洁,无需额外写判断表达式:
@OptIn(ExperimentalContracts::class) inline fun <R, T> R.applyIfNotNull(value: T?, block: R.(T) -> Unit): R { contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } value?.let { block(it) } return this }
使用示例
.applyIfNotNull(source) { nonNullSource -> // 直接拿到非空的source实例,无需额外空判断 sourceClassName = nonNullSource.className sourceMethodName = nonNullSource.methodName }
实现原理
将静态布尔值入参改为函数类型的条件后,条件判断逻辑会在applyIf函数内部执行,结合契约对调用次数的明确保证,Kotlin编译器可以正确推导:当block被执行时,condition的返回值一定为true,因此条件中包含的空检查、类型检查等状态都会被正确透传到block内部,实现和原生if判断一致的智能转换效果。
内容的提问来源于stack exchange,提问作者neph
相关产品推荐
相关产品推荐

