Swift,基于扩展协议的类不符合原始协议

时间:2017-01-18 18:08:41

标签: swift class protocols conform

这些协议让我做恶梦。

我正在尝试实现一些符合它们的协议和类,这样我就可以拥有默认实现,但通过扩展协议/类可以获得自定义实现。到目前为止,这就是我所拥有的:

protocol ProtA {
    var aProperty: String { get set }
    var anotherProperty:String { get set }

    func aFunc (anArgument: String) -> String
}

protocol ProtB: ProtA {
    var aThirdProperty: String { get set }
}

protocol ProtC {
    func doSomething(parameter: Int, with anotherParameter: ProtA)
}

class ClassA: ProtA {
    var aProperty: String = "Hello, I'm a String."
    var anotherProperty: String = "I'm not the same String."

    func aFunc (anArgument: String) -> String {
        return anArgument
    }
}

class ClassB: ProtB {

    var aProperty: String = "Hello, I'm a String."
    var anotherProperty: String = "I'm not the same String."
    var aThirdProperty: String = "I'm yet another String!"

    func aFunc (anArgument: String) -> String {
        return anArgument
    }
}



class ClassC: ProtC {

func doSomething(parameter: Int, with anotherParameter: ProtA) {
    print (anotherParameter.aProperty) // Works fine.

}

}

然后,如果我这样做

class ClassC: ProtC {

    func doSomething(parameter: Int, with anotherParameter: ProtA) {
        print (anotherParameter.aProperty) // Works fine.

    }

}

但是,如果我这样做

class ClassD: ProtC {

    func doSomething(parameter: Int, with anotherParameter: ProtA) {

        print (anotherParameter.aThirdProperty) // Value of type 'ProtA' has no member 'aThirdProperty'

    }

}

,如果我做了

class ClassE: ProtC {

    func doSomething(parameter: Int, with anotherParameter: ProtB) {

        print (anotherParameter.aThirdProperty) // Type 'ClassE' does not conform to protocol 'ProtC'

    }

}

我做错了什么?

2 个答案:

答案 0 :(得分:4)

问题

从类型继承时,无法缩小已覆盖函数中使用的参数类型。这是您通过将参数从类型ProtA(更通用的类型)更改为ProtB(更具体的类型)来完成的。

这是Liskov substitution principle的结果。简而言之,子类必须能够(至少)超类可以做的所有事情。

ProtC确定所有符合要求的类型都有func doSomething(parameter: Int, with anotherParameter: ProtA)函数,类型为(Int, ProtA) -> Void)

ClassE中的修改后的功能类型为(Int, ProtB) -> Void。但是,此功能不能再替代它覆盖的功能。

假设可以做你尝试过的事情。看看会发生什么:

let instanceConformingToProtA: ProtA = ClassA()

let instanceConformingToProtC: ProtC = ClassE()

// This would have to be possible:
instanceConformingToProtC(parameter: 0, amotherParameter: instanceConformingToProtA)

但是,ClassE() 无法instanceConformingToProtA作为其第二个参数的有效参数,因为它是ProtA不是所需的ProtB

解决方案

此问题的解决方案完全取决于您要实现的目标。我需要进一步的信息才能继续。

根据经验,当重写继承成员时:

  • 参数类型必须相同或更一般。
    • E.g。您不能使用类型为Car的参数覆盖函数,并将参数类型更改为RaceCar。这样做会破坏您的班级使用RaceCar的能力,而这必须能够通过LSP完成。
    • E.g。您可以使用类型为Car的参数覆盖函数,并将参数更改为Vehicle。这样做可以保持您的班级使用“车辆”的能力。
  • 返回类型必须相同或更具体。
    • E.g。您不能使用返回Car的函数覆盖返回类型为Vehicle的函数。这样做意味着返回的值比超级保证应该的“强大”。
    • E.g。您可以使用返回Car的函数覆盖返回类型为RaceCar的函数。这样做意味着返回的值“更强大”,并且它至少与超类守护者一样多。

答案 1 :(得分:2)

没有错。您应该确保声明在语义上是一致的。 您应该创建ProtD,使用ProtB参数声明该方法,或者解包已获取的ParamA参数以将其用作ProtB。

func doSomething(parameter: Int, with anotherParameter: ProtB) {
     if let a = anotherParameter as? ProtA {
          print (a.aThirdProperty)
     }

}