Swift:覆盖子类中的==仅在超类中调用==

时间:2015-03-01 11:53:50

标签: swift override equality

我有一个类A,它符合Equatable协议并实现==功能。在子类B中,我使用更多检查覆盖==

但是,当我在B的两个实例数组(两者都具有类型Array<A>)之间进行比较时,会调用==的{​​{1}}。当然,如果我将两个数组的类型更改为A,则会调用Array<B>的{​​{1}}。

我提出了以下解决方案:

==

看起来很丑陋,必须为B的每个子类进行扩展。有没有办法确保首先调用A.swift: internal func ==(lhs: A, rhs: A) -> Bool { if lhs is B && rhs is B { return lhs as! B == rhs as! B } return ... } 子类?

2 个答案:

答案 0 :(得分:39)

为包含A的{​​{1}}调用Array<A>的相等性的原因是自由函数的重载是静态解决的,而不是动态解析的 - 也就是说,在编译时在类型上,而不是在运行时基于指向的值。

这并不奇怪,因为B未在类中声明,然后在子类中重写。这似乎是非常有限的,但老实说,使用传统的OO技术定义多态相等是非常(并且看似困难)的。有关详细信息,请参阅this linkthis paper

天真的解决方案可能是在==中定义动态调度的函数,然后定义A来调用它:

==

然后,当您实施class A: Equatable { func equalTo(rhs: A) -> Bool { // whatever equality means for two As } } func ==(lhs: A, rhs: A) -> Bool { return lhs.equalTo(rhs) } 时,您将覆盖B

equalTo

你仍然需要进行一次class B: A { override func equalTo(rhs: A) -> Bool { return (rhs as? B).map { b in return // whatever it means for two Bs to be equal } ?? false // false, assuming a B and an A can’t be Equal } } 舞蹈,因为你需要确定右手参数是as?(如果B直接接受了equalTo,它不会是合法的覆盖。)

这里还隐藏着一些可能令人惊讶的行为:

B

也就是说,参数的顺序会改变行为。这不好 - 人们希望平等是对称的。所以你真的需要上面链接中描述的一些技术才能正确解决这个问题。

此时你可能觉得这一切都变得有点不必要了。它可能是,特别是在Swift标准库的let x: [A] = [B()] let y: [A] = [A()] // this runs B’s equalTo x == y // this runs A’s equalTo y == x 文档中给出以下注释:

  

平等意味着可替代性Equatablex == yx时   可以在任何仅依赖于它们的值的代码中互换。

     

由三等于y区分的类实例标识是   特别是不是实例值的一部分。揭露其他非价值   不建议===类型的方面, 的任何方面   暴露应该在文档中明确指出。

鉴于此,您可能会非常想重新考虑使用Equatable实施,如果您实现平等的方式是而不是,那么您对此感到满意两个相等的值相互替换。避免这种情况的一种方法是将对象标识视为衡量平等的标准,并用Equatable来实现==,只需要为超类做一次。或者,您可以问自己,真的是否需要实现继承?如果没有,请考虑抛弃它并使用值类型,然后使用协议和泛型来捕获您正在寻找的多态行为。

答案 1 :(得分:0)

我遇到了类似的问题,因为我想在MKPointAnnotation子类(继承自NSObject)上使用difference(from:)。即使我将func ==添加到注释子类中,difference(from:仍将调用NSObject的==实现。我相信这只是比较2个对象的内存位置,这不是我想要的。为了使difference(from:正常工作,我必须为我的注释子类实现override func isEqual(_ object: Any?) -> Bool {。在正文中,我将确保object与子类的类型相同,然后在其中进行比较。