以下情况:我尝试实现一个泛型函数,该函数检查变量列表是否全部都不为null并执行一个lambda,它需要不可为空的变量。
我可以使用2,3,4 ...自变量链接多个let调用或实现多个'safeLet'-Function,但是我仍然希望可以使用一个带有列表的泛型函数。
此处是带有链接的let-call的当前代码:
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之后,以避免进行nullcheck,但这很丑。
我的想法是使用Kotlin合同。可能是这样的:
@ExperimentalContracts
fun <T> List<Any?>.safeLet(block: () -> T) {
contract {
returnsNotNull() implies {ALL ELEMENTS ARE NOT NULLABLE}
}
if(this.contains(null)) return
block()
}
谢谢。
关于“ filterNotNull”注释,我现在尝试了。仍然不理想,因为我不喜欢在这里使用this [0]和this [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()
答案 0 :(得分:0)
您可以使用binding
函数。它接受另一个函数,您可以在其中使用bind
将可空引用转换为非空引用。
如果您将非空参数传递给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()
}
}