本文介绍了Swift:在子类中覆盖==仅在超类中导致==的调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've got a class A, which conforms to Equatable protocol and implements == function. In subclass B I override == with more checks.

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

However, when I do comparison between two arrays of instances of B (which both have type Array<A>), == for A is invoked. Of course if I change type of both arrays to Array<B>, == for B is invoked.

我想出了以下解决方案:

I came up with the following solution:

A.swift:

internal func ==(lhs: A, rhs: A) -> Bool {
    if lhs is B && rhs is B {
        return lhs as! B == rhs as! B
    }
    return ...
}

哪个看上去真的很丑,并且必须为A的每个子类进行扩展.有没有办法确保子类的==首先被调用?

Which looks really ugly and must be extended for every subclass of A. Is there a way to make sure that == for subclass is invoked first?

推荐答案

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

The reason the equality for A is being invoked for an Array<A> 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.

这并不奇怪,因为==没有在类内部声明,然后在子类中被覆盖.这似乎是非常有限的,但说实话,使用传统的OO技术定义多态相等性是极其困难的(并且是欺骗性的).参见此链接和以获取更多信息.

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.

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

The naïve solution might be to define a dynamically dispatched function in A, then define == to just call that:

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:

Then when you implement B, you’d override 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,则它不是合法的替代).

You still have to do one as? dance, because you need to determine if the right-hand argument is a B (if equalTo took a B directly, it wouldn’t be a legitimate override).

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

There’s also still some possibly surprising behaviour hidden in here:

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

// this runs B’s equalTo
x == y
// this runs A’s equalTo
y == x

也就是说,参数的顺序改变了行为.这不好,人们期望平等是对称的.因此,实际上您需要上面链接中描述的一些技术才能正确解决此问题.

That is, the order of the arguments changes the behaviour. This is not good – people expect equality to be symmetric. So really you’d need some of the techniques described in the links above to solve this properly.

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

At which point you might feel like all this is getting a bit unnecessary. And it probably is, especially given the following comment in the documentation for Equatable in the Swift standard library:

通过三等号===区分的类实例身份为 显然不是实例值的一部分.暴露其他非价值 不鼓励使用Equatable类型的方面,以及所有的类型 应该在文档中明确指出暴露的地方.

Class instance identity as distinguished by triple-equals === is notably not part of an instance's value. Exposing other non-value aspects of Equatable types is discouraged, and any that are exposed should be explicitly pointed out in documentation.

鉴于此,如果您实现相等性的方式不是,而您对两个值感到满意,那么您可能会认真地考虑重新考虑使用Equatable实现相等被彼此取代.避免这种情况的一种方法是将对象标识视为相等性的度量,并根据===实施==,对于超类仅需执行一次.或者,您可以问问自己,您真的需要实现继承吗?如果不是这样,请考虑放弃它,而是使用值类型,然后使用协议和泛型来捕获您要查找的多态行为.

Given this, you might seriously want to reconsider getting fancy with your Equatable implementation, if the way you’re implementing equality is not in a way where you’d be happy with two values being equal being substituted with each other. One way to avoid this is to consider object identity to be the measure of equality, and implement == in terms of ===, which only needs to be done once for the superclass. Alternatively, you could ask yourself, do you really need implementation inheritance? And if not, consider ditching it and using value types instead, and then using protocols and generics to capture the polymorphic behaviour you’re looking for.

这篇关于Swift:在子类中覆盖==仅在超类中导致==的调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-21 18:32