示例:
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
}
为什么编译器会抱怨第二个窥探者?无法解析该值不能为null吗?还是我错过了什么?
答案 0 :(得分:3)
TL; DR:签出this playground。
通过Kotlin合同,这是 sort 的一种可能-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.
现在我在这里说 sort 是因为合同的使用有一些限制。仅顶级功能支持它们。这意味着您将必须创建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
}
可以在this playground中播放完整的示例。
答案 1 :(得分:0)
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)
}