Swift - 要求实现协议的类是某个类的子类

时间:2015-01-07 07:36:03

标签: inheritance swift interface protocols typechecking

我正在创建几个NSView类,所有这些类都支持特殊操作,我们称之为transmogrify。乍一看,这似乎是协议的理想之地:

protocol TransmogrifiableView {
    func transmogrify()
}

但是,此协议强制执行每个TransmogrifiableView也是NSView。这意味着我在NSView上调用的任何TransmogrifiableView方法都不会输入check:

let myView: TransmogrifiableView = getTransmogrifiableView()
let theSuperView = myView.superView // error: TransmogrifiableView does not have a property called 'superview'

我不知道如何要求实现我的协议的所有类也是NSView的子类。我试过这个:

protocol TransmogrifiableView: NSView {
    func transmogrify()
}

但Swift抱怨协议不能从类继承。使用

将协议转换为仅类协议没有帮助
protocol TransmogrifiableView: class, NSView {
    func transmogrify()
}

我不能使TransmogrifiableView成为超类而不是协议,因为我的一些TransmogrifiableView类必须是其他非可变形视图的子类。

我应该如何要求所有TransmogrifiableView也是NSView?我真的不想用“as”转换来加密我的代码,这些转换形式不好而且分散注意力。

8 个答案:

答案 0 :(得分:17)

通过使用关联类型来强制执行子类,有一种解决方法:

protocol TransmogrifiableView {
    associatedtype View: NSView = Self
    func transmogrify()
}

class MyView: NSView, TransmogrifiableView { ... } // compiles
class MyOtherClass: TransmogrifiableView { ... } // doesn't compile

答案 1 :(得分:17)

从Swift 4开始,您现在可以将其定义如下:

let myView: NSView & TransmogrifiableView

有关详情,请查看问题#156 Subclass Existentials

答案 2 :(得分:9)

您可以使用以下内容:

protocol TransmogrifiableView where Self:NSView {}

这要求所有创建的实例符合TransmogrifiableView协议,并使用NSView进行子类化

答案 3 :(得分:5)

我认为你是在NSView的子类之后。试试这个:

protocol TransmogrifiableView {
    func transmogrify()
}

class MyNSView: NSView, TransmogrifiableView {
    // do stuff.
}

稍后在代码中接受MyNSView类型的对象。

修改

您可能需要Extension,请参阅this

extension NSView: TransmogrifiableView {
    // implementation of protocol requirements goes here
}
  • 请注意,如果没有这种额外的方法,您将无法获得NSView。
  • 您可以单独扩展NSView的子类以覆盖此新方法。

另一个选择是创建一个包含指向NSView的指针的类,并实现其他方法。这也将迫使您从NSView中代理您想要使用的所有方法。

class NSViewWrapper: TransmogrifiableView {
    var view : NSView!
    // init with the view required.
    //  implementation of protocol requirements goes here.
    .....
   // proxy all methods from NSView.
   func getSuperView(){
       return self.view.superView
   }
}

这很长很不好,但会奏效。我建议你只在你真的无法使用扩展时使用它(因为你需要NSView而不需要额外的方法)。

答案 4 :(得分:3)

根据定义,协议只声明"方法的属性,属性其他要求"。并且通过"其他要求"这意味着超类不属于它。

  

协议定义了适合特定任务或功能的方法,属性和其他要求的蓝图。

现在,我没有看到一个干净的解决方案。可以使用where - 子句来定义NSView的类型并符合TransmogrifiableView这样的类型:

class MyClass<T where T: NSView, T: TransmogrifiableView> {
    var aTransmogrifiableNSView: T
}

或者你可以使用另一个超类:

protocol TransmogrifiableViewProtocol {
    func transmogrify()
}

class TransmogrifiableView: NSView, TransmogrifiableViewProtocol {
    func transmogrify() {
        assert(false, "transmogrify() must be overwritten!")
    }
}

class AnImplementedTransmogrifiableView: TransmogrifiableView {
    func transmogrify() {
        println("Do the transmogrification...")
    }
}

最后,这两种解决方案都不干净,不能让自己满意。也许有一天abstract - 关键字会被添加到Swift中?

答案 5 :(得分:3)

对于Swift 4,基于@Antoine敏锐的洞察力:

创建协议,然后使用typealias为符合类和协议的类型提供更清晰的名称。

protocol Transmogrifiable {
    func transmogrify()
}
typealias TransmogrifiableView = NSView & Transmogrifiable

然后,您可以定义一个继承自该类型的类....

class ATransmogView: TransmogrifiableView {
    func transmogrify() {
        print("I'm transmogging")
    }
}

....或者定义一个继承自协议和子类的类

// this also qualifies as a TransmogrifiableView

class BTransmogView: NSTextView, Transmogrifiable {
    func transmogrify() {
        print("I'm transmogging too")
    }
}

现在你可以做到这一点。

func getTransmogrifiableView() -> TransmogrifiableView {
    return someBool ? ATransmogView() : BTransmogView()
}

这现在编译。

let myView: TransmogrifiableView = getTransmogrifiableView()
let theSuperView = myView.superView

答案 6 :(得分:0)

仍然不是理想的解决方案,但这是我偶尔使用的模式:

  1. 使用您要强制执行的方法定义协议。
  2. 定义您的基类,实现您希望孩子们免费获得的任何内容。
  3. 在该基类(来自#2)中,有一个可选的&#34;委托&#34;您在#1中制定的协议的变量。
  4. 定义所有子类,以便它们继承基类并实现协议。
  5. 这让你的基类调用协议方法,同时强制孩子们实现它们(例如,self.myDelegate?.myProtocolMethod)。

答案 7 :(得分:0)

在此处检查此解决方案... Swift - class method which must be overridden by subclass

此解决方案允许从类继承,也可以进行协议的编译时检查。 :)