Kotlin - 安全调用操作符的链接。不必要的接线员电话

Kotlin - chaining of safe call operator. unnecessary operator calls

以下面使用安全调用运算符 (?.) 的示例为例:

class Sample {
    class A(
            val sampleB: B? = B()
    )

    class B(
            val sampleC: C = C()
    )

    class C(
            val sampleInt: Int = 1
    )

    fun test() {
        val intInC: Int? = A().sampleB?.sampleC?.sampleInt
    }
}

我知道我们需要 sampleB 上的安全调用操作符。但是为什么我们需要安全调用运算符 on sampleC。 如果我删除该运算符,它不会编译。

根据我对运算符的理解,如果 sampleB 为空,则 returns 行为空。如果 sampleB 不为空,我们可以根据其类型确定 sampleC 不为空。但为什么 Kotlin 在 sampleC 上强制使用安全调用运算符?

A().sampleB?.sampleC?.sampleInt

解析为

((A().sampleB)?.sampleC)?.sampleInt

类型是

A(): A
A().sampleB: B?
(A().sampleB)?.sampleC: C?
((A().sampleB)?.sampleC)?.sampleInt: Int?

因为 sampleC 之前的类型是 B?,所以需要 ?.

首先,安全调用操作符?.不会打断安全调用链。当你写 A().sampleB?.sampleC?.sampleInt 时,如果 A().sampleB 为空,链将不会在 ?.sampleC 处停止,而是会执行 null?.sampleCA().sampleB?.sampleC 的类型将是 C? 而不是 C 因为它取决于整个表达式而不是 属性 的类型。这就是为什么如果前面的表达式可以为空,则需要 ?.

如果 sampleB 是链中唯一可为空的表达式,您可以考虑使用 .run:

val intInC: Int? = A().sampleB?.run { sampleC.sampleInt }

我猜你不相信,因为正如你所说,如果它到达链中调用 .sampleC 的点,那是因为 sampleB 不为空,如果它为空它不会达到这一点。

这里的重点是您是从实施的角度考虑的,从这个意义上说,您可能是对的。但是您需要从语义上解释它:即使出于优化的原因,假设它在之前找到空值时不需要转到行尾,但整行仍然需要具有连贯的含义,即使在那里是空值。为此,您需要 ? 符号。