简体   繁体   English

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

[英]Swift, classes based on extended protocol don't conform to original protocol

These protocols are giving me nightmares.这些协议让我做噩梦。

I am trying to implement a couple protocols and classes conforming to them, such that I can have default implementations, but customized implementations are available by extending the protocols/classes.我正在尝试实现几个符合它们的协议和类,这样我就可以有默认实现,但是通过扩展协议/类可以使用自定义实现。 So far, this is what I have:到目前为止,这就是我所拥有的:

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.

}

}

Then, if I do那么,如果我这样做

class ClassC: ProtC {

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

    }

}

But, if I do但是,如果我这样做

class ClassD: ProtC {

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

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

    }

}

and, if instead I do而且,如果我这样做

class ClassE: ProtC {

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

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

    }

}

What am I doing wrong?我究竟做错了什么?

The problem问题

When inheriting from a type, you cannot narrow down the types of the parameters used in overridden functions.从类型继承时,您不能缩小覆盖函数中使用的参数的类型。 This is what you've done by changing the parameter from type ProtA (a more general type), to ProtB (a more specific type).这就是您通过将参数从类型ProtA (更通用的类型)更改为ProtB (更具体的类型) ProtB

This is a consequence of the Liskov substitution principle .这是Liskov 替换原则的结果 Simply put, a subclass must be able to do ( at minimum ) everything that the superclass can do.简单地说,子类必须能够(至少)做超类可以做的所有事情。

ProtC establishes that all conforming types have a function func doSomething(parameter: Int, with anotherParameter: ProtA) , with type (Int, ProtA) -> Void) . ProtC确定所有符合的类型都有一个函数func doSomething(parameter: Int, with anotherParameter: ProtA) ,类型为(Int, ProtA) -> Void)

Your modified function in ClassE has type (Int, ProtB) -> Void .您在ClassE修改的函数类型为(Int, ProtB) -> Void However, this function can no longer act as a substitute for the one it overrides.但是,此函数不能再替代它所覆盖的函数。

Suppose that it was possible to do what you tried.假设有可能做你尝试过的事情。 Watch what would happen:看看会发生什么:

let instanceConformingToProtA: ProtA = ClassA()

let instanceConformingToProtC: ProtC = ClassE()

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

But, ClassE() can't take instanceConformingToProtA as a valid argument to its second parameter, because it's a ProtA , not the required ProtB .但是, ClassE()不能instanceConformingToProtA作为其第二个参数的有效参数,因为它是ProtA而不是必需的ProtB

The solution解决方案

The solution to this problem is entirely dependant on what you're trying to achieve.此问题的解决方案完全取决于您要实现的目标。 I would need further information before being able to proceed.在继续之前,我需要更多信息。

As a rule of thumb, when overriding inherited members:根据经验,当覆盖继承的成员时:

  • Parameter types must be the same, or more general.参数类型必须相同,或更通用。
    • Eg you can't override a function with a parameter of type Car , and change the parameter type to RaceCar .例如,您不能用Car类型的参数覆盖函数,并将参数类型更改为RaceCar Doing so breaks your classes ability to work with RaceCar s, which it must be able to do by the LSP.这样做会破坏您的类使用RaceCar的能力,而 LSP 必须能够做到这一点。
    • Eg you can override a function with a parameter of type Car , and change the parameter to Vehicle .例如,您可以使用Car类型的参数覆盖函数,并将参数更改为Vehicle Doing so preserves your classes' ability to work with `Vehicles.这样做可以保留您的类使用“车辆”的能力。
  • Return types must be the same, or more specific.返回类型必须相同或更具体。
    • Eg you can't override a function with a return type of Car with a function that returns Vehicle .例如,您不能用返回Vehicle的函数覆盖返回类型为Car的函数。 Doing so would mean the returned value is "less powerful" than the super class guarantees it should be.这样做意味着返回的值比超类保证的“功能更弱”。
    • Eg you can override a function with a return type of Car with a function that returns RaceCar .例如,您可以使用返回RaceCar的函数覆盖返回类型为Car的函数。 Doing so would mean the returned value is "more powerful", and it does at least as much as what the super class guarentees.这样做意味着返回的值“更强大”,并且它的作用至少与超类所保证的一样多。

There's nothing wrong.没有什么不对的。 You should just make sure that the declarations are semantically consistent.您应该确保声明在语义上是一致的。 You should either create ProtD declaring the method with a ProtB parameter OR unwrapping the gotten ParamA parameter to use it as ProtB.您应该创建 ProtD,使用 ProtB 参数声明方法,或者解开获取的 ParamA 参数以将其用作 ProtB。

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

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM