如果没有显式 null 检查,编译器无法识别不可为空的值



示例:

class Object(val value: Int?) {
   fun doesNotContainNull() = value != null
} 
Object obj = Object(randomValue)

然后,当我使用此对象时,我可以这样做一个零检查:


if (obj.value != null) {
  passNonNullableValue(obj.value) // compiles
}

或喜欢这个

if (obj.doesNotContainNull()) {
  passNonNullableValue(obj.value) // does not compile
}

为什么编译器抱怨第二个狙击手?不能解决该值不能无效吗?还是我错过了什么?

这是通过Kotlin合同 - Kotlin 1.3的实验特征,以增强智能演员的支持。

这是contract的一个简单示例:

@ExperimentalContracts
fun Object?.isNotNull(): Boolean {
    contract {
        // If I return "true", then it means that I am not null
        returns(true) implies (this@isNotNull != null)
    }
    return this != null
}
if (obj.isNotNull())
    print(obj.value) // smart-cast applied.

现在,我在这里说,因为合同的使用有一些局限性。只有顶级功能支持它们。这意味着您必须创建Object类的扩展名。另一个限制是,我们要在此处检查Object.value的无效性,而不是Object本身。合同定义不支持这。因此,即使看起来很理想,以下内容也没有编译:

@ExperimentalContracts
fun Object.doesNotContainNull(): Boolean {
    contract {
        // The following line gives an error:
        // Error in contract description: only references to parameters are allowed in contract description.
        returns(true) implies (this@doesNotContainNull.value != null)    }
    return value != null
}

正如错误所暗示的那样,我们可以参考合同描述中的参数。因此,我们可以提出一个解决方法,在该解决方案中,我们将value作为参数传递。最后,该函数看起来如下:

@ExperimentalContracts
fun Object.doesNotContainNull(value: Int?): Boolean {
    contract {
        returns(true) implies (value != null)
    }
    return value != null
}
if (obj.doesNotContainNull(obj.value)) {
    passNonNullableValue(obj.value) // smart cast applied!
}

即使这种方法有效,这也很危险,因为呼叫者可能并不总是将obj.value作为参数传递。因此,如果支票失败,我们希望确保条件并丢下运行时错误。

@ExperimentalContracts
fun Object.doesNotContainNull(value: Int?): Boolean {
    contract {
        // "If I return true, then it means that $value is not null."
        returns(true) implies (value != null)
    }
    if(value != this.value)
        throw IllegalArgumentException("$value must be ${this.value}")
    return value != null
}

完整的示例可以在此操场上进行。

kotlin 编译器不知道调用 doesNotContainNull()的含义。为了提供此信息,引入了kotlin.contracts。尽管此API仍然有一些快捷方式,但您可以使用sealed class方法将其抽象。

sealed class Object {
    abstract val value: Int?
    class NotNull(override val value: Int) : Object()
    object Null : Object() {
        override val value: Int? = null
    }
    companion object {
        operator fun invoke(value: Int?) = if (value == null)
            Object.Null else Object.NotNull(value)
    }
} 
fun Object.doesNotContainNull(): Boolean {
    contract {
        returns(true) implies (this@doesNotContainNull is Object.NotNull)
    }
    return value != null
}

可以像您一样实现一切,就可以使用。

有点冗长。

,但最终使用?.let可能更清楚地阅读和理解。

obj.value?.let {
    passNonNullableValue(it)
}

相关内容

  • 没有找到相关文章

最新更新