如何覆盖在子类中具有关联类型参数的方法?

时间:2016-09-27 23:21:04

标签: swift associated-types

我试图做的事情:

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.
}

错误是:

  

Method不会覆盖其超类

中的任何方法

如果我尝试使用ItemType:

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

错误是:

  

'的ItemType'在此上下文中类型查找是不明确的

有没有办法让这项工作?

3 个答案:

答案 0 :(得分:2)

这是一种做你想做的事情的方法:

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 :(得分:2)

这里的问题不是相关类型,方法输入是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方法的原因是方法输出是协变。因此,使用() -> Element方法覆盖() -> BarElement方法是完全合法的,因为即使您将Bar实例向上转换为FooBarElement实例也会从getElement返回{1}}可以自由地转发到Element

至于解决方案,它取决于您的确切用例。您可能尝试做的事情根本不需要继承,而是可以将FooBar分别与HasElement相符。< / p>

答案 2 :(得分:0)

这有效:

interface IUser
{
     int UserId { get; set; }
     string UserName { get; set; }
}

internal IQueryable<T> GetUsers<T>()
    where T : IUser, new()
{
    var result = from u in db.Users
         select new T()
         {
            UserID = u.ID,
            UserName = u.Name
         };
    return result;
}

使用override func getElement() -> BarElement { ... } 关键字是指函数签名未发生变化(override仍为BarElement)。您可以从具有相同函数名,相同参数名和相同返回值类型的超类复制和粘贴函数。

但是

Element

失败,因为更改参数类型(具有相同的参数名称)时。 这种情况是重载而不是覆盖。