Kotlin call function only if all arguments are not null

前端 未结 5 1106
太阳男子
太阳男子 2021-01-08 01:05

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 bo         


        
相关标签:
5条回答
  • 2021-01-08 01:16

    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).

    0 讨论(0)
  • 2021-01-08 01:19

    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")
    }
    
    0 讨论(0)
  • 2021-01-08 01:27

    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.

    0 讨论(0)
  • 2021-01-08 01:35

    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 ->
        ...
    }
    
    0 讨论(0)
  • 2021-01-08 01:39

    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).

    0 讨论(0)
提交回复
热议问题