8

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

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 不为空,我们可以根据它的类型确定 sampleC 不为空。但是为什么 Kotlin 会在 sampleC 上强制使用安全调用运算符?

4

3 回答 3

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

解析为

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

类型是

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

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

于 2017-11-18T05:05:12.007 回答
2

首先,安全调用算子?.不会破坏安全调用链。当你写的时候A().sampleB?.sampleC?.sampleInt,如果A().sampleB为null,则链不会停在,?.sampleC而是会执行null?.sampleC。的类型A().sampleB?.sampleC将是C?但不是C,因为它取决于整个表达式而不是属性的类型。?.如果前一个表达式可以为空,这就是需要的原因。

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

val intInC: Int? = A().sampleB?.run { sampleC.sampleInt }
于 2017-11-18T06:34:31.883 回答
0

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

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

于 2017-11-18T11:19:29.813 回答