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

kov*_*pas 27 overriding equality swift

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

但是,当我在两个实例数组B(两者都有类型Array<A>)之间进行比较时,调用==for A.当然,如果我改变两个阵列的类型Array<B>,==对于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 ...
}
Run Code Online (Sandbox Code Playgroud)

这看起来很丑陋,必须扩展到每个子类A.有没有办法确保==首先调用子类?

Air*_*ity 41

AArray<A>包含的B内容调用相等的原因是自由函数的重载是静态解决的,而不是动态解析的 - 也就是说,在编译时基于类型,而不是在运行时基于指向的值.

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

天真的解决方案可能是定义一个动态调度的函数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)
}
Run Code Online (Sandbox Code Playgroud)

然后在实施时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
    }
}
Run Code Online (Sandbox Code Playgroud)

你还要做一个as?舞蹈,因为你需要确定右手参数是一个B(如果equalTo采取了B直接,它不会是一个合法的覆盖).

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

let x: [A] = [B()]
let y: [A] = [A()]

// this runs B’s equalTo
x == y
// this runs A’s equalTo
y == x
Run Code Online (Sandbox Code Playgroud)

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

在这一点上,您可能会觉得这一切都变得有点不必要了.它可能是,特别是Equatable在Swift标准库的文档中给出以下注释:

平等意味着可替代性.何时x == y,x并且 y 可以在任何仅依赖于其值的代码中互换.

由三等于区分的类实例标识=== 尤其不是实例值的一部分.暴露的其他非增值环节Equatable的类型是气馁,任何 暴露应该被文件明确指出.

鉴于此,你可能真的想重新考虑对你的Equatable实现有所了解,如果你实现相等的方式不是你对两个相等的值相互替换感到满意的方式.避免这种情况的一种方法是将对象标识视为相等的度量,并==根据===需要实现,对于超类只需要执行一次.或者,你可以问自己,你真的需要实现继承吗?如果没有,请考虑放弃它并使用值类型,然后使用协议和泛型来捕获您正在寻找的多态行为.