2016-09-27 31 views
0

我试图这样做:如何覆盖子类中具有关联类型参数的方法?

protocol HasElement { 
    associatedtype ItemType 
    func getElement() -> ItemType 
    func setElement(element: ItemType) 
} 

class Element {} 
class BarElement: Element {} 

class Foo: NSObject, HasElement { 
    typealias ItemType = Element 
    func getElement() -> Element { ... } 
    func setElement(element: Element) { ... } 
} 

class Bar: Foo { 
    typealias ItemType = BarElement 
    override func getElement() -> BarElement { ... } // This works. 
    override func setElement(element: BarElement) { ... } // This fails. 
} 

的错误是:

方法不会覆盖任何方法,其超

如果我尝试使用ItemType的,而不是:

override func setElement(element: ItemType) { ... } // Still fails. 

错误是:

“的ItemType”不明确的类型查找在这方面

有没有一种方法,使这项工作?

+0

您是否尝试将它设置为Element?应该少一些“含糊” –

+0

然后我不得不将它用在子类中。这个练习的重点在于不必一直抛出困难的东西。 –

+0

但是,这确实有效。 –

回答

1

这里有一种方法做你想要什么:

protocol HasElement { 
    associatedtype ItemType 
    func getElement() -> ItemType 
    func setElement(element: ItemType) 
} 

class Element {} 
class BarElement: Element {} 

class Foo: HasElement { 
    // no need for typealias, the associated type is inferred 
    func getElement() -> Element { return Element() } 
    func setElement(element: Element) { } 
} 

class Bar: Foo { 
    // no need for typealias, the associated type is inferred 
    override func getElement() -> BarElement { return BarElement() } 

    // hide the parent class method 
    @available(*, unavailable, message: "Use setElement(element: BarElement)") 
    override func setElement(element: Element) { } 

    // comply with protocol in this class 
    func setElement(element: BarElement) { } 
} 

// can't do this now: 
let myElement = Element() 
let myBar = Bar() 
myBar.setElement(element: myElement) // Error: 'setElement(element: BarElement)' is unavailable: Use setElement(element: BarElement) 
+1

有趣的解决方案,但请注意,通过重载而不是重写,您会失去动态调度,这可能会或可能不需要。如果将'myBar'定义为'let myBar:Foo = Bar()',那么我希望调用'Foo'的'setElement(element:)'方法,而不是'Bar'方法。虽然有趣的是,当这样做时,两种方法似乎都不会被调用 - 直到您删除不可用的属性。 – Hamish

+1

我同意它可能不是最好的解决方案。我认为继承可能不会成为现实,相反,基于协议的方法可能会带来更好的结果。 – ColGraff

1

这里的问题不相关的类型,那就是方法输入是contravariant。因此,您不能重写一个方法,该方法需要使用需要子类实例输入的方法来输入给定的超类实例。

事实上,你可以简单地熬代码到:

class Element {} 
class BarElement : Element {} 

class Foo { 
    func setElement(element: Element) { } 
} 

class Bar : Foo { 
    // error: Method does not override any method from its superclass 
    override func setElement(element: BarElement) { } 
} 

你根本不能覆盖(Element) -> Void方法有(BarElement) -> Void方法。如果您考虑创建Bar实例并将其上传到Foo会发生什么情况,原因应该是相当明显的。您现在可以将Element实例传递给期望BarElement实例的方法,这是非法的。

它适用于您的getElement方法的原因是方法输出为covariant。因此,使用() -> BarElement方法覆盖() -> Element方法是完全合法的,因为即使您将Bar实例上传到Foo,但从getElement返回的BarElement实例也可以自由地上传到Element

至于解决方案,它取决于你的确切用例。很可能你所要做的并不需要继承,而是可以分别将FooBar分别符合HasElement

+0

是的,够公平的。 –