为什么子协议不符合其父协议?

时间:2019-01-07 18:58:09

标签: swift

我有符合协议的结构,但是使用派生协议,而不是直接使用其父协议:

protocol A { }
protocol B: A { }

protocol C {
    var property: A { get }
}

struct Foo: C {
    let property: A
}

struct Bar: C {
    let property: B
}

// Error: Type 'Bar' does not conform to protocol 'C'

为什么Bar不符合要求,因为propertyA的子协议。

1 个答案:

答案 0 :(得分:2)

B描述了符合某些规则集的类型。它不是本身符合这些规则的类型。 B不符合B,更不用说需要进一步遵守的任何内容了。 protocol B:A说:“符合B的任何内容也必须符合A。”但是,B不符合B,因此也不符合A

关于SO的答案已经很多次了,但是要重复“为什么”,最简单地归结为以下示例:

protocol A { 
    init()
}

func f(type: A.Type) {
    let a = type.init()
}

f(type: A.self)

如果A本身符合A,那么这应该合法。但是,init应该调用什么f?在存在initstatic要求的情况下,协议不可能符合自己。

虽然原则上可以在这种情况下实现所需的特定协方差,但是Swift不能区分合法和非法协方差,因此无法全部使用。例如,考虑从不变到可变的微小变化:

protocol C {
    var property: A { get set }
}

在这种情况下,Bar绝对不可能遵循(它必须为接受{em> any property的{​​{1}}提供一个设置器。尽管getter会返回A的子类型)。 Swift无法区分所有可能的情况(通常是所有事物都是不可变的,并且没有Ainit要求)和不可能的情况。在技​​术上可行的情况下,已经进行了一些讨论,但令人担忧的是,对协议进行很小的更改可能会破坏您的整个类型结构,有时是出于非显而易见的原因。相反,至少到目前为止,类型要求通常是不变的。


解决此整体问题的一种典型方法是仅提供所需类型的访问器。例如:

static

这提供了更大的灵活性(您可以根据需要将protocol A { } protocol B: A { } protocol C { var aProperty: A { get } } struct Foo: C { let aProperty: A } struct Bar: C { var aProperty: A { return bProperty } let bProperty: B } 设为bProperty),并使代码更明确。