Kotlin合同可帮助编译器智能广播所有列表元素,从可为空到不可为空?

时间:2019-11-07 07:53:24

标签: kotlin

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

1 个答案:

答案 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()
    }
}