Kalle Richter
Kalle Richter

Reputation: 8728

Does an ? operator on a non-null property prevent any NullPointerException in the chain?

I'm having trouble understanding the following change to a piece of Kotlin code:

package org.example.kotlin.nulll.resolution

import java.util.*

object UtilResources {

    private var properties: Properties? = null

    fun loadProperties() {
        properties = Properties()
        properties?.load(UtilResources.javaClass.getResourceAsStream("/config.properties"))
    }

    fun getProperties(properties: String): String {
        loadProperties()
        System.out.println(UtilResources.properties)
        System.out.println(UtilResources.properties?.getProperty(properties))
        return UtilResources.properties?.getProperty(properties).toString()
    }
}

works fine, i.e. returns the property value loaded from config.properties or "null" in case the property isn't present in the file.

If I change the code to be null-safe to

object UtilResources {

    private val properties: Properties = Properties()

    fun loadProperties() {
        properties.load(UtilResources.javaClass.getResourceAsStream("/config.properties"))
    }

    fun getProperties(properties: String): String {
        loadProperties()
        System.out.println(UtilResources.properties)
        System.out.println(UtilResources.properties.getProperty(properties))
        return UtilResources.properties.getProperty(properties).toString()
    }
}

I'm getting NullPointerException because UtilResources.properties.getProperty(properties) is null which I'm verifying with the System.out.println Statements.

In the first version properties is not null just like in the second, so the ? operator is the only thing that's changing afaik. However, it's placed after a non-null property, so it shouldn't have any effect.

null.toString() should always work because it's an overloading extension function in Kotlin.

Assume that config.properties exists and that UtilResources.javaClass.getResourceAsStream("/config.properties" returns a non-null value. You can find an SSCCE project at https://gitlab.com/krichter-sscce/kotlin-null-resolution. It doesn't contain more information than this question.

Please note that I'm not seeking debugging support, I'd like to understand what's going on and broaden my understanding of Kotlin. I didn't manage to condense the example further.

I'm using Kotlin 1.4.31 through Maven 3.6.

Upvotes: 3

Views: 270

Answers (1)

Benjamin
Benjamin

Reputation: 78

In your first version Kotlin knows that you are calling toString() on a nullable type, since you are calling getProperty()on the Nullable field properties, wherefore Kotlins extension function Any?.toString() ist used.

In your second version Kotlin doesn't knows that you are calling toString()on a Nullable type, since you are calling getProperty()on a field that cannot be null and the function getProperty() is a Java function that doesn't defines a Nullable return value. Therefore the extension function isn't used and a NPE is thrown if the property doesn't exists.

The following code works as expected:

object UtilResources {

    private val properties: Properties = Properties()

    fun loadProperties() {
        properties.load(UtilResources.javaClass.getResourceAsStream("/config.properties"))
    }

    fun getProperties(key: String): String {
        loadProperties()
        println(properties)
        println(properties.getProperty(key))
        return properties.getOptionalProperty(key).toString()
    }

    private fun Properties.getOptionalProperty(key: String): String? = getProperty(key)
}

Upvotes: 3

Related Questions