Kotlin: Multi variable null check

Different ways of checking for not null with multiple variables

Posted by Jamie Craane on Thursday, December 10, 2020

Although not-null, immutable variables with sensible defaults are often desired, it is not always the case. It is sometimes required to check if multiple variables are not null. This post describes several variants of how to do this when smart casts are not possible because the variables are mutable.

Consider the following example:

class Person() {
    var name: String? = null
    var age: Int? = null

    fun doSomething() {
        if (name != null && age != null) {
            name.length // Compile error since smart-cast is not possible
        }
    }
}

When the name or age property is accessed after the above null check we get the following compile error: smart cast to ‘String’ is impossible, because ‘name’ is a mutable property that could have been changed by this time.

There are several solutions to this problem which are presented below.

Assign variables to immutable val

In this variant the mutable variables are assigned to immutable val. This wat the compiler is able to execute the smart cast:

class Person {
    var name: String? = null
    var age: Int? = null

    fun doSomething() {
        val n = name
        val a = age
        if (a != null && n != null) {
            n.length // Smart cast is possible
        }
    }
}

Use destructuring to assign the variables to a val

In the example the destructuring capabilities of Kotlin are used to make assigment of multiple variables at once easier. The null-check itself is the same:

class Person {
    var name: String? = null
    var age: Int? = null

    fun doSomething() {
        val (n, a) = name to age
        if (a != null && n != null) {
            n.length
        }
    }
}

In the above example the to keyword is used to create a pair which is destructured into n and a. If there are more than two variables the listOf() method can be used to destructure up to 5 variables (because a list defines component1 to component5 functions) as in the following example:

val (a, b, c, d, e) = kotlin.collections.listOf("a", "b", "c", "d", "e")

Use a specialized function

The following example uses a function to check the passed-in variables for not-null and executes the block of both variables are not null:

/**
 * Executes block if all supplied params are not null.
 *
 * @param a First param
 * @param b Second param
 * @param block The block to execute of all of the supplied params are not null.
 */
fun <A, B, RESULT> doIfNotNull(a: A?, b: B?, block: (A, B) -> RESULT): RESULT? {
    if (a != null && b != null) {
        return block(a, b)
    }

    return null
}

The above function can be used like this:

class Person {
    var name: String? = null
    var age: Int? = null

    fun doSomething() {
        doIfNotNull(name, age) { n, a -> n.length}
    }
}

For a more in-depth discussion see https://discuss.kotlinlang.org/t/kotlin-null-check-for-multiple-nullable-vars/1946