Kotlin合约帮助编译器将所有列表元素从可为null智能转换为不可为null



以下情况:我尝试实现一个泛型函数,该函数检查变量列表是否全部不为null,并执行lambda,该函数需要不可为null的变量。

我可以链接多个let调用或实现多个带有2,3,4…参数的"safeLet"-函数,但我仍然希望一个带有列表的泛型函数是可能的。

这里是当前代码,带有链式let调用:

val parameters = call.receiveParameters()
val firstName = parameters["firstName"]
val lastName = parameters["lastName"]
firstName?.let {
lastName?.let { userService.add(UserDTO(firstName = firstName, lastName = lastName)) }
}

这是我当前的"safeLet"功能:

fun  <T> List<Any?>.safeLet(block: () -> T) {
if(this.contains(null)) return
block()
}

但以下仍然没有编译(因为UserDTO的参数是String而不是String?(:

listOf(firstName, lastName).safeLet {
userService.add(UserDTO(firstName = firstName, lastName = lastName))
}

我可以添加!!在firstName和lastName之后,以避免null检查,但这很难看。

我的想法是使用kotlin合同。像这样的事情可能吗:

@ExperimentalContracts
fun  <T> List<Any?>.safeLet(block: () -> T) {
contract {
returnsNotNull() implies {ALL ELEMENTS ARE NOT NULLABLE}
}    
if(this.contains(null)) return
block()
}

提前谢谢。


关于"filterNotNull"注释,我现在尝试了这个。仍然不理想,因为我不喜欢在这里使用这个[0]和这个[1],但它有效:

allNotNull(firstName, lastName)?.apply {
userService.add(UserDTO(firstName = this[0], lastName = this[1]))
}

fun <T : Any> allNotNull(vararg elements: T?): List<T>? = if(elements.contains(null)) null else elements.filterNotNull()

您可以使用binding函数。它接受另一个函数,在该函数中可以使用bind将可为null的引用转换为非null引用。

如果将一个非null参数传递给bind,它将返回该参数。否则,它将挂起binding块的执行。

如果暂停执行,则binding返回null,否则返回binding块的结果。


以下是如何使用binding:

binding { userService.add(UserDTO(firstName = firstName.bind(), lastName = lastName.bind())) }

再举一个例子:

fun sumOrNull(a: Int?, b: Int?): Int? = binding { a.bind() + b.bind() }

这是我的binding实现:

// startCoroutineUninterceptedOrReturn returns either COROUTINE_SUSPENDED or R
@Suppress("UNCHECKED_CAST")
fun <R> binding(block: suspend Binder.() -> R): R? =
when (val result = block.startCoroutineUninterceptedOrReturn(Binder, BinderContinuation)) {
COROUTINE_SUSPENDED -> null
else -> result as R
}
@RestrictsSuspension
object Binder {
suspend fun <T> T?.bind(): T {
if (this != null) return this
suspendCoroutine<Nothing> {}
}
}
suspend fun <T> Binder.bind(obj: T?): T {
contract {
returns() implies (obj != null)
}
return obj.bind()
}
private object BinderContinuation : Continuation<Any?> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Any?>) {
result.getOrThrow()
}
}

最新更新