Search code examples
listkotlinnull

How to verify no null elements in list, Kotlin


I'd like to check a List<Int?> for nulls and if no nulls exist, then allow the List to proceed as a List<Int>, otherwise use an emptyList()

This is what I came up with:

var noNullElements: List<Int> = emptyList()
if (listWithPossibleNullElements.all { it != null }) {
    noNullElements = listWithPossibleNullElements as List<Int>
}
//do something with noNullElements list

I'd be shocked if this was the best, most idiomatic way.

Solution

There are many acceptable solutions below, but the one I chose was a combination of the solutions

fun getSafeList(list: List<Int?>): List<Int> =if (null in list) emptyList() else list.requireNoNulls()

Solution

  • Here's a one liner extension function:

    inline fun <reified T : Any> Iterable<T?>.filterNoneNull(): Iterable<T>? =
      takeIf { null !in it }?.requireNoNulls()
    
    • takeIf {} - a scope function that will return null if the lambda evaluates to false
    • null !in it - use the in operator function to determine if the list contains null
    • finally, if takeIf {} returns a non-null value, filterNotNull() will convert the element type from the nullable T? to non-null T

    Note that it will return null if the provided Iterable<T?> contains null, else List<T>, in case it's important to differentiate between the provided list being empty, or containing null. An alternative can be provided using the elvis operator.

    Example usage - run in Kotlin Playground:

    fun main() {
      val listA: List<Int?> = listOf(1, 2, 3, null)
    
      // since listA contains 'null', the result is 'null'
      println(listA.filterNoneNull())
      // prints: null
    
      // provide an alternative, using the elvis operator
      println(listA.filterNoneNull() ?: emptyList<Int>())
      // prints: []
    
      val listB: List<Int> = listOf(1, 2, 3)
    
      // since listB does not contain null, the same list is returned
      println(listB.filterNoneNull())
      // prints: [1, 2, 3]
    }
    
    inline fun <reified T : Any> Iterable<T?>.filterNoneNull(): Iterable<T>? =
      takeIf { null !in it }?.requireNoNulls()
    

    Note: I edited the example and replaced filterNotNull() with requireNoNulls(), as the latter will not instantiate a new list