一个吗?非空 属性 上的运算符可防止链中出现任何 NullPointerException?

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

我无法理解对一段 Kotlin 代码的以下更改:

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()
    }
}

工作正常,即 returns 从 config.properties"null" 加载的 属性 值,以防文件中不存在 属性。

如果我将代码更改为空安全的

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()
    }
}

我得到 NullPointerException 因为 UtilResources.properties.getProperty(properties)null 我正在用 System.out.println 语句验证。

在第一个版本中 properties 与第二个版本中的 null 不同,因此 ? 运算符是唯一改变 afaik 的东西。但是,它位于非空 属性 之后,因此应该不会有任何影响。

null.toString() 应该始终有效,因为它是 Kotlin 中的重载扩展函数。

假设 config.properties 存在并且 UtilResources.javaClass.getResourceAsStream("/config.properties" returns 是一个非空值。您可以在 https://gitlab.com/krichter-sscce/kotlin-null-resolution 找到 SSCCE 项目。它不包含比这个问题更多的信息。

请注意,我不是在寻求调试支持,我想了解正在发生的事情并拓宽我对 Kotlin 的理解。我没能进一步压缩示例。

我正在使用 Kotlin 1.4.31 到 Maven 3.6。

在您的第一个版本中,Kotlin 知道您在可空类型上调用 toString(),因为您在可空字段 properties 上调用 getProperty(),因此 Kotlin 扩展函数 Any?.toString() 已使用。

在你的第二个版本中,Kotlin 不知道你在调用 toString() 一个 Nullable 类型,因为你在一个不能是 null 的字段上调用 ​​getProperty() 并且function getProperty() 是一个 Java 函数,它没有定义 Nullable return 值。因此,如果 属性 不存在,则不使用扩展函数并抛出 NPE。

以下代码按预期工作:

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)
}