Kotlin call function only if all arguments are not null

China☆狼群 提交于 2019-12-09 02:46:22

问题


Is there a way in kotlin to prevent function call if all (or some) arguments are null? For example Having function:

fun test(a: Int, b: Int) { /* function body here */ }

I would like to prevent null checks in case when arguments are null. For example, for arguments:

val a: Int? = null
val b: Int? = null 

I would like to replace:

a?.let { b?.let { test(a, b) } }

with:

test(a, b)

I imagine that function definition syntax could look something like this:

fun test(@PreventNullCall a: Int, @PreventNullCall b: Int)

And that would be equivalent to:

fun test(a: Int?, b: Int?) {
    if(a == null) {
        return
    }

    if(b == null) {
        return
    }

    // function body here
}

Is something like that (or similar) possible to reduce caller (and possibly function author) redundant code?


回答1:


If you don't want callers to have to do these checks themselves, you could perform null checks in an intermediary function, and then call into the real implementation when they passed:

fun test(a: Int?, b: Int?) {
    a ?: return
    b ?: return
    realTest(a, b)
}

private fun realTest(a: Int, b: Int) {
    // use params
}

Edit: here's an implementation of the function @Alexey Romanov has proposed below:

inline fun <T1, T2, R> ifAllNonNull(p1: T1?, p2: T2?, function: (T1, T2) -> R): R? {
    p1 ?: return null
    p2 ?: return null
    return function(p1, p2)
}

fun test(a: Int, b: Int) {
    println("$a, $b")
}

val a: Int? = 10
val b: Int? = 5
ifAllNonNull(a, b, ::test)

Of course you'd need to implement the ifAllNonNull function for 2, 3, etc parameters if you have other functions where you need its functionality.




回答2:


You could define your own inline function for it.

inline fun <R, A> ifNotNull(a: A?, block: (A) -> R): R? =
    if (a != null) {
        block(a)
    } else null

inline fun <R, A, B> ifNotNull(a: A?, b: B?, block: (A, B) -> R): R? =
    if (a != null && b != null) {
        block(a, b)
    } else null

inline fun <R, A, B, C> ifNotNull(a: A?, b: B?, c: C?, block: (A, B, C) -> R): R? =
    if (a != null && b != null && c != null) {
        block(a, b, c)
    } else null

inline fun <R, A, B, C, D> ifNotNull(a: A?, b: B?, c: C?, d: D?, block: (A, B, C, D) -> R): R? =
    if (a != null && b != null && c != null && d != null) {
        block(a, b, c, d)
    } else null

And then you can call it like

ifNotNull(a, b, c, d) { a, b, c, d ->
    ...
}



回答3:


NO! is the answer to your question(as far as I know)

Your best bet(assuming the function is not exposed) is what you said.

a?.let { b?.let { test(a, b) } }

If the function is exposed, and might be called without these checks, then you need to put the checks inside your function.

fun test(a: Int?, b: Int?) {
    if (listOf(a, b).filterNotNull().size < 2) return

    println("Function was called")
}



回答4:


If you try assigning null to any of the two Int variables you will find that this doesn`t work. See the compile errors in the comments.

fun test(a: Int, b: Int) { /* function body here */ }

fun main(){
    test(null, 0) //Null can not be value of non-null type Int
    val b : Int? = null
    test(0, b) // Type mismatch. Required: Int - Found: Int?
}

The example shows that in a pure Kotlin world test(a: Int, b: Int) cannot be called with null or even Int? arguments. If you put Java in the mix I doubt there is a safe solution without null checks, because you can call test(Int, Int) with type Integer from the Java side, which allows null. The Java "equivalent" to Int would be @NotNull Integer (which is not really null-safe).




回答5:


Let for Tuples (Pair & Triple)

I think the spirit of the OP was syntax, so my answer focuses on providing "let" for tuple types:

Example use:

fun main() {
    val p1: Int? = 10 // or null
    val p2: Int? = 20 // or null
    val p3: Int? = 30 // or null

    val example1 = (p1 to p2).let(::testDouble)
    val example2 = (p1 to p2).let { a, b -> a * b }

    val example3 = (p1 to p2 to p3).let(::testTriple)
    val example4 = (p1 to p2 to p3).let { a, b, c -> a * b * c }
}

fun testDouble(a: Int, b: Int): Int {
    return a + b
}

fun testTriple(a: Int, b: Int, c: Int): Int {
    return a + b + c
}

Extension Funs:

// Define let for Pair & Triple
fun <P1, P2, R> Pair<P1?, P2?>.let(f: (P1, P2) -> R): R? {
    return f(first ?: return null, second ?: return null)
}

fun <P1, P2, P3, R> Triple<P1?, P2?, P3?>.let(f: (P1, P2, P3) -> R): R? {
    return f(first ?: return null, second ?: return null, third ?: return null)
}

// Cute "to" syntax for Triple
infix fun <P1, P2, P3> Pair<P1?, P2?>.to(third: P3?): Triple<P1?, P2?, P3?> {
    return Triple(first, second, third)
}

You can replace "to" with another word (see Triple extension as a guide), and you could extend to larger tuples (but Kotlin only provides 2 out-of-the-box & sadly I don't think it can be generic).



来源:https://stackoverflow.com/questions/50742094/kotlin-call-function-only-if-all-arguments-are-not-null

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!