采用以下使用安全呼叫操作符(?。)的示例:
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上?
答案 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?.sampleC
。 A().sampleB?.sampleC
的类型将是C?
,但不是C
,因为它取决于整个表达式,但不取决于属性的类型。这就是为什么如果前面的表达式可以为空,则需要?.
。
如果sampleB
是链中唯一可为空的表达式,您可以考虑使用.run
:
val intInC: Int? = A().sampleB?.run { sampleC.sampleInt }
答案 2 :(得分:0)
我猜你不相信,因为正如你所说,如果它到达链中.sampleC
被调用的点,因为sampleB
不是null,如果它是null,它会没有达到这一点。
这里的要点是你在考虑实施方面,从这个意义上说,你可能是正确的。但是你需要在语义上解释它:尽管,出于优化原因,我们可以说,当它在找到null之前不需要到达行尾,但是,即使在那里,整行也需要具有连贯的含义是null。为此,您需要?
符号。