Kotlin: Convert variables to non nullable if condition is true

Andy Res :

What I mean is following.

Consider this code:

// Can be a Long or null
val data1= param1.toLongOrNull()
val data2= param2.toLongOrNull()
val dataN= paramN.toLongOrNull()

// Proceed with the action if ALL of the data are not nulls
if(notNull(data1, data2, dataN)){
    // do something with data1,2,N
}

notNull() is a utility function that accepts a list of variable arguments. Returns true if all of the arguments are not nulls.

fun <T> notNull(vararg elements: T): Boolean {
    elements.forEach {
        if (it == null) {
            return false
        }
    }
    return true
}

The issue I am having is that Kotlin does not know that inside the if block, the data1,2,N cannot be null. As a result, this fails to compile:

if(notNull(data1, data2, dataN)){
     data1 + data2 + dataN
     // Fail: Required Long, Found Long?
     // Operator + not allowed on nullable receiver.
}

On the other hand, if the variables are explicitly checked against null, then Kotlin knows this is fine and does not complain.

if(data1!=null && data2!=null && dataN!=null){
     data1 + data2 + dataN
}

It would be nice to "configure" the notNull method in such way, so that Kotlin knows that once it returns true, any of the passed parameters cannot be null inside the block.
Is this possible?

Mark Keen :

Obviously multiple ways to skin a cat, but you could do something like this :

inline fun <T> doIfNotNull(elements: Array<T?>, block : (Array<T>) -> Unit) {
    val validElements = arrayListOf<T>()
    for (i in elements) {
       if (i == null) return
       validElements.add(i)
    }
    block.invoke(validElements.toArray(elements))
}

Using with varargs (has to be last parameter, making the receiver function first arg, which doesn't feel as nice);

inline fun <T> doIfNotNull(block : (Array<T>) -> Unit, vararg elements : T?) {
    val validElements = arrayListOf<T>()
    for (i in elements) {
       if (i == null) return
       validElements.add(i)
    }
    block.invoke(validElements.toArray(elements))
}

Example :

fun test() {
    val elements = arrayOf(1L, 2L, null)
    doIfNotNull(elements, {
        it.sum()
    })
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=101772&siteId=1