Swift 2.0:参数化的 class 如果它继承自 class 是可等式的,则不会调用适当的 == 函数

Swift 2.0: Parametrized classes don't call proper == function if it inherits from class that is Equatable

当参数化 class 继承自另一个符合 Equatable 的 class 时,== 调用超级 class 的 == 。谁能解释为什么会这样 and/or 我怎么可能在这里做错了什么?我相信一个例子最能说明我的问题:

public class Foo: Equatable {}
public func ==(lhs: Foo, rhs: Foo) -> Bool { return false }

//Parametrized
public class Bar<T: Equatable>: Foo {
  public var bar: T?
  public init(barIn: T?) {
    self.bar = barIn
  }
}
public func ==<T>(lhs: Bar<T>, rhs: Bar<T>) -> Bool { return lhs.bar == rhs.bar }

//Non parametrized
public class Baz: Foo {
  public var baz: Int?
  public init(bazIn: Int?) {
    self.baz = bazIn
  }
}
public func ==(lhs: Baz, rhs: Baz) -> Bool { return lhs.baz == rhs.baz }

//Parametrized, no inheritance
public class Qux<T: Equatable>: Equatable {
  public var qux: T?
  public init(quxIn: T?) {
    self.qux = quxIn
  }
}
public func ==<T>(lhs: Qux<T>, rhs: Qux<T>) -> Bool { return lhs.qux == rhs.qux }

Bar<Int>(barIn: 1) == Bar<Int>(barIn: 1) //false
Baz(bazIn: 1) == Baz(bazIn: 1) //true
Qux(quxIn: 1) == Qux(quxIn: 1) //true, of course

不能说我是 Swift 运算符重载方面的专家,但我发现 an article 你可以找到一些有用的信息:

For reference types, the equality becomes conflated with identity. It makes sense that two Name structs with the same values would be equal, but two Person objects can have the same name, but be different people.

For Objective-C-compatible object types, the == operator is already provided from the isEqual: method: .... For Swift reference types, equality can be evaluated as an identity check on an ObjectIdentifier constructed with an instance of that type:

请也考虑这个 :

The reason the equality for A is being invoked for an Array that contains B is that overloading of free functions is resolved statically, not dynamically – that is, at compile time based on the type, not at runtime based on the pointed-to value.

This is not surprising given == is not declared inside the class and then overridden in the subclass. This might seem very limiting but honestly, defining polymorphic equality using traditional OO techniques is extremely (and deceptively) difficult. See this link and this paper for more info.

The naïve solution might be to define a dynamically dispatched function in A, then define == to just call that: ... Then when you implement B, you’d override equalTo:

因此,您的代码无法正常工作的原因可能是编译器在 静态 解析后调用它,而这是在不知道您会 override == 运算符,用于 Foo.
的某些锚点 可能你应该这样做(相等逻辑移动到从运算符调用的函数:

public class Foo: Equatable {
  func equalTo(rhs: Foo) -> Bool {
    // base logic here
  }
}
public func ==(lhs: Foo, rhs: Foo) -> Bool {
  // check for type here and call appropriate function]
  // may be this will be done automatically as Bar overloads equalTo function
  return lhs.equalTo(rhs)
}

public class Bar<T: Equatable>: Foo {
  public var bar: T?
  public init(barIn: T?) {
    self.bar = barIn
  }
  override func equalTo(rhs: Foo) {
    // cast rhs to Foo here
    // if it can't be done, return false
    return (rhs as? Foo).map { foo in
      return self.bar == foo.bar
    } ?? false
  }
}

虽然我在 Swift 参考资料中没有找到任何关于此的内容,this gives us a clue:

Generics are lower down the pecking order. Remember, Swift likes to be as “specific” as possible, and generics are less specific. Functions with non-generic arguments (even ones that are protocols) are always preferred over generic ones:

虽然这似乎与 Equatable 没有任何关系;该测试向我们展示了相同的行为:

class Foo {};
class Bar<T>: Foo {};
class Baz: Bar<Int> {};
class Qux<T>: Baz {};

func test(foo: Foo) {
    print("Foo version!");
};

func test<T>(bar: Bar<T>) {
    print("Bar version!");
};

func test(baz: Baz) {
    print("Baz version!");
};

func test<T>(qux: Qux<T>) {
    print("Qux version!");
};

let foo = Foo();
let bar = Bar<Int>();
let baz = Baz();
let baz2: Bar<Int> = Baz();
let qux = Qux<Float>();

test(foo);  // Foo
test(bar);  // Foo
test(baz);  // Baz
test(baz2); // Foo
test(qux);  // Baz

所以这里发生的事情是 在选择一个自由函数时,以及使用它的静态类型而不是动态类型时,Swift 不喜欢使用任何泛型],即使那个泛型是类型参数而且确实应该是最专业的选择。

所以,似乎要解决这个问题,正如@VMAtm 所建议的那样,您应该将 equalTo 之类的方法添加到 class 中,以便实际方法在运行时间。