当参数化类继承自符合Equatable
的另一个类时,==
会调用超类的==
。任何人都可以解释为什么会发生这种情况和/或我在这里做错了什么?我相信一个最好的例子说明了我的问题:
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
答案 0 :(得分:3)
虽然我没有在Swift参考文献中找到任何关于此的内容,this gives us a clue:
泛型在啄食顺序下降。请记住,Swift喜欢尽可能“具体”,而泛型则不那么具体。具有非泛型参数的函数(甚至是协议的函数)总是优先于泛型参数:
这似乎与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
的方法,以便在运行时获取实际的方法。
答案 1 :(得分:2)
不能说我是Swift运算符重载的专家,但我发现an article你可以找到一些有用的信息:
对于引用类型,相等性与身份相混淆。它 有意义的是,具有相同值的两个Name结构将是相等的, 但是两个Person对象可以具有相同的名称,但不同 人
对于Objective-C兼容的对象类型,==运算符已经存在 从
isEqual
:方法提供: .... 对于Swift引用类型,可以将相等性评估为标识 检查使用该实例构造的ObjectIdentifier
类型:
请同时考虑这个answer for similar problem:
为数组调用A的相等性的原因 包含B 解析了自由函数的重载 静态地,不是动态的 - 也就是说,在编译时基于 类型,而不是在运行时基于指向的值。
这并不奇怪,因为==未在类中声明 然后在子类中重写。这可能看起来非常有限但是 老实说,使用传统的OO定义多态相等 技术极其(并且具有欺骗性)困难。见this link和 this paper了解更多信息。
天真的解决方案可能是定义动态调度 函数在A中,然后定义
==
来调用它:...然后当你 实施B,你覆盖equalTo
:
因此,可能是您的代码无法正常运行,因为编译器在 statical 解析之后调用它,而不知道您将覆盖==的某个安慰者的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
}
}