如何让Kotlin编译器知晓已在函数内完成空值检查?
解决Kotlin编译器无法识别自定义空值检查函数的问题
这个问题我之前也碰到过,Kotlin的智能空值分析默认只会识别内置的空值检查逻辑(比如直接写possibleNull != null),不会自动理解你自定义的testNull函数做了什么。不过咱们可以用Kotlin的**契约(Contracts)**来解决这个问题,让编译器明确知晓你的检查逻辑。
方案一:使用Kotlin契约(推荐)
Kotlin的契约是给编译器的一种提示,能让它理解函数的行为和参数之间的关系。具体步骤如下:
- 启用实验性契约API:因为目前Contracts还是实验性特性,你需要添加
@OptIn(ExperimentalContracts::class)注解(或者在模块配置中开启实验性特性,但注解方式更直接)。 - 在
testNull函数中添加契约声明:明确说明当函数返回true时,传入的参数一定是非空的。
修改后的代码示例:
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @OptIn(ExperimentalContracts::class) private fun stuff() { var possibleNull: String? = "test" if (testNull(possibleNull)) { // 现在编译器会自动推断possibleNull是非空的,无需!! mustPassNonNull(possibleNull) } } private fun mustPassNonNull(possibleNull: String) { // 使用非空值 } @OptIn(ExperimentalContracts::class) private fun testNull(possibleNull: String?): Boolean { contract { // 告诉编译器:当函数返回true时,possibleNull != null成立 returns(true) implies (possibleNull != null) } return possibleNull != null }
这样修改后,编译器就能识别出if (testNull(possibleNull))代码块内的possibleNull已经完成了空值检查,不会再提示你未做空值校验。
方案二:无需实验性API的替代方案
如果你不想使用实验性特性,也可以调整testNull的使用方式,通过takeIf和let的组合让编译器识别非空性:
private fun stuff() { var possibleNull: String? = "test" possibleNull.takeIf { testNull(it) }?.let { nonNullValue -> mustPassNonNull(nonNullValue) } } private fun mustPassNonNull(possibleNull: String) { // 使用非空值 } private fun testNull(possibleNull: String?): Boolean { return possibleNull != null }
这个方案通过takeIf筛选出通过空值检查的参数,再用let将非空值传递给mustPassNonNull,编译器也能识别nonNullValue的非空性,不过相比契约方案,代码会稍微繁琐一点。
内容的提问来源于stack exchange,提问作者casolorz
相关产品推荐
相关产品推荐

