使一个协议符合另一个协议

时间:2018-06-21 10:44:29

标签: ios swift protocols

我有两个协议: InstrumentForProfessional 。我想将任何 Pen 设置为 InstrumentForProfessional

protocol Pen {
  var title: String {get}
  var color: UIColor {get}
}

protocol Watch {} // Also Instrument for professional
protocol Tiger {} // Not an instrument

protocol InstrumentForProfessional {
  var title: String {get}
}

class ApplePen: Pen {
  var title: String = "CodePen"
  var color: UIColor = .blue
}

extension Pen: InstrumentForProfessional {} // Unable to make ApplePen an Instument for Professional: Extension of protocol Pen cannot have an inheritance clause

let pen = ApplePen() as InstrumentForProfessional

5 个答案:

答案 0 :(得分:6)

这是您要求遵守扩展协议的方式。

extension Pen where Self: InstrumentForProfessional {}

当前的操作方式使编译器认为您正在进行继承,而不是协议一致性。

还要注意,let pen = ApplePen() as InstrumentForProfessional没有意义,也不会编译。

答案 1 :(得分:4)

Protocols can inherit each other

  

协议继承

     

协议可以继承一个或多个其他协议,并且可以在继承的要求之上添加更多要求。协议继承的语法类似于类继承的语法,但是可以选择列出多个继承的协议,并用逗号分隔:

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // protocol definition goes here
}

因此,您基本上需要这样做:

protocol InstrumentForProfessional {
    var title: String {get}
}

protocol Pen: InstrumentForProfessional {
    var title: String {get} // You can even drop this requirement, because it's already required by `InstrumentForProfessional`
    var color: UIColor {get}
}

现在,所有符合Pen的事物也都符合InstrumentForProfessional

答案 2 :(得分:2)

已经提供了两个答案:@ user28434为您提供了一个解决方案,假设您可以在编写Pen协议时添加一致性,并且@ paper1111为您提供了一个机会在Pen扩展名之外添加类型也符合InstrumentForProfessional的扩展名。注意:要利用@ paper1111的答案,还必须像这样将协议添加到您的类型中:

class ApplePen: Pen, InstrumentForProfessional {
  var title: String = "CodePen"
  var color: UIColor = .blue
}

与@ user28434的答案相比,这似乎与您的要求相去甚远,并且实际上在回答一个不同的问题(即如何向采用两种不同协议的类型添加功能)。因此,我想问一问,您实际上在寻找的不是协议而是类继承:

class InstrumentForProfessional {
    var title: String
    init(title:String) {
        self.title = title
    }
}

class Pen: InstrumentForProfessional {
    var color: UIColor
    init(title:String, color:UIColor) {
        self.color = color
        super.init(title: title)
    }
}

因为在这两者中都存在title属性,这似乎是类继承共有的重写行为。因此,问题就变成了为什么在使用class而不是structenum时为什么要努力将类继承压缩到协议中?

如果您不想应用类继承,并且在编写Pen协议时不想添加继承,并且您也不想在类中添加多个协议,那么您可以做的另一件事就是使用typealias:

protocol InstrumentForProfessional {
    var title: String {get}
}

protocol PenExtra {
    var color: UIColor {get}
    var title: String {get}
}

typealias Pen = InstrumentForProfessional & PenExtra

class ApplePen: Pen {
    var title = "CodePen"
    var color = UIColor.blue
}

但是写完所有这些之后,如果您可以遵循@ user28434的方法,那么就可以这样做。

答案 3 :(得分:1)

我认为,如果您查看此SO答案,将会解决相同的问题。

https://stackoverflow.com/a/37353146/1070718

@ paper1111接近您要寻找的内容,但我认为您确实想这样做:

extension InstrumentForProfessional where Self: Pen {}

由于Pen已经符合InstrumentForProfessional,所以您只需要在它是Pen时扩展InstrumentForProfessional。

有时候我忘记了协议继承在Swift中是如何工作的,但是感谢SO刷新了我的内存。

答案 4 :(得分:1)

以上所有答案都说明了如何去做,但没有说明为什么

在考虑协议时,您必须进行心理转换 - 协议不是结构。当你定义协议一致性时,你只给出了一个一致性类型必须打包的一组必需的东西。给予或接受该类型将如何实现它们。

protocol InstrumentForProfessional {
  var title: String {get}
}

protocol Pen: InstrumentForProfessional {
  var title: String {get}
  var color: UIColor {get}
}

protocol Watch: InstrumentForProffesional {}
protocol Tiger {} // Not an instrument

笔不符合 InstrumentForProfessional。 InstrumentForProfessional。另一个示例是拥有 Instrument 协议和 StringInstrument。你知道,StringInstrument 不符合 Instrument;它一个工具。

我认为您正在寻找的是某些字段的默认实现。考虑这个例子;每个移动对象都应该告诉它的最大速度。汽车是移动的物体吗?是的。它应该符合MovingObject吗? 不!

protocol MovingObject {
    /// Top speed a vehicle can reach in km/h.
    var topSpeedKMH: Double { get }
}

protocol Car: MovingObject {
    /// Horsepower of a car.
    var hp: Double { get }
    var weight: Double { get }
    
    // Note that topSpeed is also required,
    // but since we specified it in MovingObject we don't have
    // to rewrite it here.
    // var topSpeedKMH: Double { get }
}

但我们知道我们可以根据马力和重量计算最高速度。这就是我们创建默认实现的原因。

extension Car {
    var topSpeedKMH: Double {
        hp * weight
    }
}

现在每辆车都可以符合“开箱即用”的移动车辆;然而,它仍然可以为每个给定的字段提供自己的实现。