Swift: Overriding == in Subclass Results Invocation of == in Superclass Only

Swift: Overriding == in subclass results invocation of == in superclass only

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.

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:

class A: Equatable {
func equalTo(rhs: A) -> Bool {
// whatever equality means for two As
}
}

func ==(lhs: A, rhs: A) -> Bool {
return lhs.equalTo(rhs)
}

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
}
}

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.

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:

Equality implies substitutability. When x == y, x and y
are interchangeable in any code that only depends on their values.

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.

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.

Overriding equals with generic class

As Airspeed suggests the problem is that operator's implementation is not a part of class/struct implementation => hence, inheritance does not work there.

What you can do is to keep the logic inside of the class implementation and make operators use it. E.g. the following will do what you need:

class MySuperClass: Equatable {

func isEqualTo(anotherSuperClass: MySuperClass) -> Bool {
fatalError("Must override")
}

}

func == (lhs: MySuperClass, rhs: MySuperClass) -> Bool {
return lhs.isEqualTo(rhs)
}

class MySubClass<T>:MySuperClass {

let id: Int

init(_ id: Int) {
self.id = id
}

override func isEqualTo(anotherSuperClass: MySuperClass) -> Bool {
if let anotherSubClass = anotherSuperClass as? MySubClass<T> {
return self.id == anotherSubClass.id
}

return super.isEqualTo(anotherSuperClass) // Updated after AirSpeed remark
}

}

let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)

a == b
b == c

... as you can see == operator is defined only once and it uses MySuperClass's method to figure out whether its two arguments are equal. And after that .isEqualTo() handles the rest, including the use of inheritance mechanism on MySubClass level.

UPD

The benefit of above approach is that the following will still work:

let a2: MySuperClass = a
let b2: MySuperClass = b
let c2: MySuperClass = c

a2 == b2
b2 == c2

... i.e. regardless of the variable type at compile-time the behaviour will be determined by the actual instance type.

Swift override protocol methods in sub classes

Use protocol extension with where clause. It works.
But I would not recommend you to have such things in your codebase.

class BaseViewController: UIViewController {

}

extension OptionsDelegate where Self: BaseViewController {
func handleSortAndFilter(opt: Options) {
print("Base class implementation")
}
}

extension BaseViewController: OptionsDelegate {

}

class InsipartionsViewController: BaseViewController {

}

extension OptionsDelegate where Self: InsipartionsViewController {
func handleSortAndFilter(opt: Options) {
print("Inspirations class implementation")
}
}

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

Though I didn't find anything on the Swift reference about this, 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:

This doens't seem to have any relation to Equatable, though; this test shows us the same behaviour:

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

So what is happening here is that when choosing a free function, along with using its static type instead of the dynamic type, Swift prefers not to use any generics, even if that generic is a type parameter and indeed it should be the most specialized choice.

So, it seems that to solve the issue, as suggested by @VMAtm, you should add a method like equalTo to the class instead, so that the actual method is picked up at runtime.

Swift calling subclass's overridden method from superclass

Make sure that the object's class is SubclassViewController. Otherwise, it will not have any knowledge of the method which is overriden by subclass

How to call the superclass implementation of an overridden method from inside the superclass in Java?

You could move said method body to a private method and let the default method (the one which may be overridden by the subclass) delegate to the former. See this example

public abstract class SuperClass {

private Object field1;

protected SuperClass(Object obj){
// call the safe implementation
setField1Safe(obj);
}


public void setField1(Object obj){
// just delegates
setField1Safe(obj);
}

private void setField1Safe(Object obj){
// perform some check on obj and
// then set field1 such that field1==obj
}
}

public class SubClass extends SuperClass{
public SubClass(Object obj){
super(obj);
}

@Override
public void setField1(Object obj){
super.setField1(obj);
// do some work that is necessary only when
// field1 is set through SubClass.setField1()
}
}

That way the sub class can still override setField1 but if you really depend on the implementation then you can call the private setField1Safe method.



Related Topics



Leave a reply



Submit