Change nullability of list elements without copying list

38 Views Asked by At

I'm calling a third-party function (which I can't change) which returns a list of nullable elements (List<Int?>), but I know for certain that all elements are non-null. I then want to pass this list to another third-party function, which takes a list of non-null elements (List<Int>). If I cast it, as I do in the code below, I get a warning. If I use filterNotNull it copies the list unnecessarily. How can I avoid copying the list and also not triggering an unchecked cast warning?

fun foo() {
    val a = bar(includeNullElements = false)
    baz(a.map { it!! })     // Undesirable to copy large list
    baz(a.filterNotNull())  // Undesirable to copy large list
    baz(a as List<Int>)     // Gives unchecked cast warning
}

/**
 * This function comes from a library that I can't change.
 * Returns a very large list of elements known to be non-null,
 * but unfortunately are typed as nullable.
 */
fun bar(includeNullElements: Boolean): List<Int?> = TODO()

/**
 * This function comes from a library that I can't change.
 * Takes a list of non-null elements.
 */
fun baz(a: List<Int>) {
    println(a)
}

2

There are 2 best solutions below

0
Karsten Gabriel On BEST ANSWER

What you can do if you absolutely don't want to copy the list, is that you cast the list to a non-nullable list, you suppress the warning for an unchecked cast, but you don't do that in an unsafe way, because you check the list before casting:

fun <T: Any> List<T?>.asNonNullable(): List<T> {
    if(any { it == null }) throw IllegalArgumentException("unexpected null element")
    @Suppress("UNCHECKED_CAST")
    return this as List<T>
}
1
Skizo-ozᴉʞS ツ On

To avoid the unchecked cast warning you can use filterIsInstance

baz(a.filterIsInstance<Int>())

Also you can use toList() that performs a shallow copy of the list, which is more efficient than using map or filterNotNull in this scenario.

See this article of shallow vs deep copy for more information about copy.