Kalle Richter
Kalle Richter

Reputation: 8728

How to check whether a List contains null in Kotlin?

I'm having trouble understanding why

class Main {
    private val outputStreams: List<OutputStream>

    @JvmOverloads constructor(outputStreams: List<OutputStream> = LinkedList()) {
        if(outputStreams.contains(null)) {
            throw IllegalArgumentException("outputStreams mustn't contain null")
        }
        this.outputStreams = outputStreams
    }
}

causes the compilation error ...Main.kt:[12,26] Type inference failed. The value of the type parameter T should be mentioned in input types (argument types, receiver type or expected type). Try to specify it explicitly..

If I use outputStreams.contains(null as OutputStream)) the compilation succeeds, but Main(LinkedList<OutputStream>()) fails at runtime due to

kotlin.TypeCastException: null cannot be cast to non-null type java.io.OutputStream
    at kotlinn.collection.contains.nulll.check.Main.<init>(Main.kt:12)
    at kotlinn.collection.contains.nulll.check.MainTest.testInit(MainTest.kt:13)

which leaves me with no other approach for keeping the code as close to the original Java as possible which is my intend as well as understanding this issue as it is rather than searching for a workaround.

Upvotes: 4

Views: 10024

Answers (3)

Pitos
Pitos

Reputation: 867

filterNotNull() method can filter out all null objects from the collection so you could use the code below. Obviously, you need to inform the compiler that objects in the collection are nullable. I changed the OutputStream type to String for easier testing.

    class Main{
private val outputStreams: List<String?>

@JvmOverloads constructor(outputStreams: List<String?> = LinkedList()) {

    if(outputStreams.filterNotNull().size < outputStreams.size) {
        throw IllegalArgumentException("outputStreams mustn't contain null")
    }
    this.outputStreams = outputStreams
}

fun getOutputStream(): List<String?>{
    return outputStreams
}
 }

A second approach would be to use let T.let(block: (T) -> R): R where you would accept the nullable object, but then you would have to check whether there is any "null" string and react accordingly.

    class Main{
lateinit var outputStreams: List<String?>

@JvmOverloads constructor(outputStreams: List<String?> = LinkedList()) {

    outputStreams.let {
        this.outputStreams = outputStreams
    }

}

fun getOutputStream(): List<String?>{
    return outputStreams
}
}

    fun main(args: Array<String>) {
val outputStreamWithNull: List<String?> = listOf("alpha", "beta", null, "omega")
val mainA = Main(outputStreamWithNull)


mainA.getOutputStream().forEach {
    println(it)
}

}

If you are "confident" that the object parameter in the constructor should be a collection of non-null objects, then you can remove ? However, the nullable check on the List collection must be done by an object initialising Main class, so you would basically move the responsibility of catching NullPointerException to someone else.

Upvotes: 1

s1m0nw1
s1m0nw1

Reputation: 81889

For the compiler, the parameter outputStreams cannot contain null as its type is List<OutputStream> as opposed to List<OutputStream?>. The type system does not expect null to be inside this list, thus no need to check it.

On the other hand, IF that parameter actually could contain null (since it comes from a Java caller) you should mark it as nullable explicitly: List<OutputStream?>

Upvotes: 4

ToraCode
ToraCode

Reputation: 432

I belive that the answer is List<OutputStream?>. ? will make your list can contain null. Check the doc: enter link description here

Upvotes: 3

Related Questions