2016-09-27 43 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

是的,夠公平的。 –