Search code examples
swiftinheritanceequatable

Swift subclass conform to equatable called super class implemenation?


class Base: Equatable {
    static func == (lhs: Base, rhs: Base) -> Bool {
        lhs.id == rhs.id
    }
    let id: String
    init(id: String) {
        self.id = id
    }
}

class SubClass: Base {
    public var id2: String?
    public init(id1: String, id2: String? = nil) {
        self.id2 = id2
        super.init(id: id1)
    }
    static func == (lhs: SubClass, rhs: SubClass) -> Bool {
        lhs.id2 == rhs.id2 && lhs.id == rhs.id
    }
}
print(a != b) // result: false
// Calls `Base` class's static func ==
print(a == b) // result: false
// Calls `SubClass` class's static func ==

I have a simple super class and subclass, subclass inherits Base and also implements static func ==

When calling a != b, it calls Base class's == implementation instead of SubClass's == implementation, why? But when calling a == b, it actually call's SubClass's == implementation, why?

I expect both != and == calls SubClass's == implementation


Solution

  • This problem existed long time ago since the improvement of Swift language (https://github.com/apple/swift-evolution/blob/master/proposals/0091-improving-operators-in-protocols.md)

    And the author also mentioned about this problem. You can check that. https://github.com/apple/swift-evolution/blob/master/proposals/0091-improving-operators-in-protocols.md#class-types-and-inheritance

    One of the solution I found is define an isEqual function.

    class Superclass: Equatable {
        var foo: Int
    
        init(foo: Int) {
            self.foo = foo
        }
    
        func isEqual(to instance: Superclass) -> Bool {
            return self.foo == instance.foo
        }
    
        static func ==(lhs: Superclass, rhs: Superclass) -> Bool {
            return type(of: lhs) == type(of: rhs) && lhs.isEqual(to: rhs)
        }
    }
    
    class Subclass: Superclass {
        var bar: String
    
        init(foo: Int, bar: String) {
            self.bar = bar
            super.init(foo: foo)
        }
    
        override func isEqual(to instance: Superclass) -> Bool {
            guard let instance = instance as? Subclass else {
                return false
            }
            return self.foo == instance.foo && self.bar == instance.bar
        }
    }
    
    let a = Subclass(foo: 1, bar: "a")
    let b = Subclass(foo: 1, bar: "b")
    
    print(a == b) // False
    print(a != b) // True
    
    

    Reference: