Kotlin - 安全呼叫运营商的链接。不必要的操作员呼

时间:2017-11-18 03:15:11

标签: kotlin kotlin-android-extensions kotlin-interop kotlin-null-safety

采用以下使用安全呼叫操作符(?。)的示例:

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上安全调用操作符。但为什么我们需要在sampleC 上使用安全调用运算符。 如果我删除该运算符,则无法编译。

根据我对运算符的理解,如果sampleB为null,则该行返回null。如果sampleB不为null,我们可以确定sampleC不是null,基于它的类型。但是为什么Kotlin强制安全调用操作符在sampleC上?

3 个答案:

答案 0 :(得分:6)

A().sampleB?.sampleC?.sampleInt

解析为

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

类型是

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

由于sampleC之前的类型是B?,因此需要?.

答案 1 :(得分:2)

首先,安全呼叫运营商?.不会破坏安全呼叫链。当您编写A().sampleB?.sampleC?.sampleInt时,如果A().sampleB为空,则链不会停在?.sampleC,但会执行null?.sampleCA().sampleB?.sampleC的类型将是C?,但不是C,因为它取决于整个表达式,但不取决于属性的类型。这就是为什么如果前面的表达式可以为空,则需要?.

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

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

答案 2 :(得分:0)

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

这里的要点是你在考虑实施方面,从这个意义上说,你可能是正确的。但是你需要在语义上解释它:尽管,出于优化原因,我们可以说,当它在找到null之前不需要到达行尾,但是,即使在那里,整行也需要具有连贯的含义是null。为此,您需要?符号。