1

我有一个具有可为空属性的类description: String?,为了方便起见,我想公开一个fun hasDescription(): Boolean对说字符串进行空检查的类。

为了更方便,我希望使用合约,以便在检查后可以智能description转换为不可为空。StringhasDescription()

  1. 现在这可能吗(带有实验性合同和 Kotlin 1.5)?
  2. 当合约稳定时有可能吗?
data class Widget (
    val description: String? = null,
)


class Gadget {
    val widget: Widget? = null

    val description: String? get() = widget?.description

    @ExperimentalContracts
    fun hasDescription(): Boolean {
        // Error in contract description: only references to
        // parameters are allowed in contract description
        contract {
            returns(true) implies((this@Gadget).description is String)
        }

        return this.description != null
    }
}

@ExperimentalContracts
fun testContract(gadget: Gadget) {
    if (gadget.hasDescription()) {
        // Error: Type mismatch (String? for String)
        // expected, since the contract itself does not compile
        val description: String = gadget.description
    }
}
4

1 回答 1

0

简而言之,这是不可能的。

契约主要考虑方法的行为而不是值的属性。值的属性应该由类型系统而不是契约来处理。

是的,它可能会在未来实施。

这并不意味着如果类型系统可以处理某些事情,那么它就超出了合同的范围。

https://github.com/Kotlin/KEEP/blob/master/proposals/kotlin-contracts.md#scope-and-restrictions

备择方案

Null 在 Kotlin 的类型系统中得到了很好的处理,因此我们可以使用它来检查可空性。

智能铸造

使用智能铸造将消除对合同和hasDescription() 方法的需求。

fun nullCheckWhen(gadget: Gadget) {
  when (val description = gadget.description) {
    null -> println("description is null")
    else -> println("description.length ${description.length}")
  }
}
fun earlyReturn(gadget: Gadget) {
  val description = gadget.description ?: return

  println("description.length ${description.length}")
}
fun forcefulNullCheck(gadget: Gadget) {

  val description = requireNotNull(gadget.description) {
    "description must not be null"
  }

  println("description.length ${description.length}")
}
fun elvisScopeFunctionNullSafe(gadget: Gadget) {

  gadget.widget?.description?.let { description ->
    println("description.length ${description.length}")
  }

}

这些有点笨拙,并且不像合同那样漂亮 - 但至少它们有效。

事实上,他们中的一些人使用 Kotlin Contracts,但他们当然只能对方法参数起作用。

Preconditions.kt

/**
 * Throws an [IllegalArgumentException] if the [value] is null. Otherwise returns the not null value.
 */
@kotlin.internal.InlineOnly
public inline fun <T : Any> requireNotNull(value: T?): T {
  contract {
    returns() implies (value != null)
  }
  return requireNotNull(value) { "Required value was null." }
}

类型层次结构

我认为这种方法不适用于您的情况,但我想我会分享它,因为它可能有用。

如果description在接口中定义为 a String?,则Widget可以实现description为 non-nullable String。这是因为在 Kotlin 中可空类型是不可空类型的超类型。的任何实现都description可以选择为 beString?String.

interface Component {
  val description: String?
}


data class Widget(
  override val description: String
) : Component


data class Greeble(
  override val description: String?
) : Component


fun nullCheckGeneric(component: Component) {

  when (component) {
    is Widget  ->
      // no need for a null check
      println("description.length ${component.description.length}")
    is Greeble ->
      // description is nullable
      println("description.length ${component.description?.length}")
  }

}
于 2022-02-06T09:02:30.933 回答