一尘不染

您如何实现返回协变Selfs的协议方法?

swift

错误:非最终类(’Class’)无法满足协议’Protocol’要求’instance’,因为它在非参数,非结果类型位置使用’Self’

protocol Protocol {
    var instance: Self {get}
}

class Class: Protocol {
    var instance: Class {return Subclass()}
}

class Subclass: Class {}

这就是我用C#表达我想要的方式。(据我所知,C#无法强制通用参数“ Self”实际上是我们从Swift知道的Self,但是它的功能足以使我做正确的事情。)

interface Protocol<Self> where Self: Protocol<Self> {
    Self instance {get;}
}

class Class: Protocol<Class> {
    public Class instance {get {return new Subclass();}}
}

class Subclass: Class {}

…在Swift的未来版本中可能看起来如何:

protocol Protocol {
    typealias FinalSelf: Protocol where FinalSelf.FinalSelf == FinalSelf

    var instance: FinalSelf {get}
}

class Class: Protocol {
    var instance: Class {return Subclass()}
}

class Subclass: Class {}

我如何模拟与我的问题相关的部分:

protocol Protocol: ProtocolInstance {
    static var instance: ProtocolInstance {get}
}

protocol ProtocolInstance {}


class Class: Protocol {
    static var instance: ProtocolInstance {return Subclass()}
}

class Subclass: Class {}

而且,我相信这是代码的相关部分:

protocol Protocol {
    static var 🎁: Self? {get} // an existing instance? 
    static var 🐥: Self {get}  // a new instance

    func instanceFunc()
}

extension Protocol {
    static func staticFunc() {
        (🎁 ?? 🐥).instanceFunc()
    }
}

阅读 198

收藏
2020-07-07

共1个答案

一尘不染

正如它所说,您不能这样做,这是有充分理由的。您无法证明自己会信守诺言。考虑一下:

class AnotherSubclass: Class {}
let x = AnotherSubclass().instance

因此,xAnotherSubclass根据您的协议(即Self)进行操作。但实际上是Subclass,这是完全不同的类型。除非课程是,否则您无法解决这个悖论final。这不是Swift的限制。此限制在任何正确的类型系统中都将存在,因为它允许类型矛盾。

另一方面,您可以做的是保证instance在所有子类(即超类)中返回某种一致的类型。您可以使用关联的类型来执行此操作:

protocol Protocol {
    typealias InstanceType
    var instance: InstanceType {get}
}

class Class: Protocol {
    var instance: Class {return Subclass()}
}

class Subclass: Class {}
class AnotherSubclass: Class {}
let x = AnotherSubclass().instance

现在x无疑是type Class。(它也碰巧是随机的其他子类,这很奇怪,但这就是代码所说的。)

顺便说一句,所有这些通常表明您确实应该使用子类化。组合和协议可能会在Swift中更好地解决此问题。问问自己,是否有任何Subclass实际需要成为的子类的原因Class。可以是符合相同协议的独立类型吗?当您摆脱子类并专注于协议时,各种问题都会消失。


我一直在考虑这个问题,也许可以找到您想要的东西。与其说所有子类都实现instanceinstance不如将其作为扩展。如果您想返回其他内容,您仍然可以覆盖它。

protocol Protocol {
    init()
}

class Class: Protocol {
    required init() {}
    var instance: Class { return Subclass() }
}

extension Protocol {
    var instance: Self { return self.dynamicType.init() }
}

class Subclass: Class {}

这避免了继承问题(您不能通过AnotherClass这种方式创建相同的“ 返回错误的类型”)。

2020-07-07