以下情况:我尝试实现一个泛型函数,该函数检查变量列表是否全部不为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()
}
}