How to properly implement the Equatable protocol in a class hierarchy?

前端 未结 4 983
不知归路
不知归路 2021-01-01 19:53

I\'m trying to implement the == operator (from Equatable) in a base class and its subclasses in Swift 3. All of the classes will only be used in Sw

4条回答
  •  天命终不由人
    2021-01-01 20:35

    I know it's been a while since the question is posted, but I hope my answer helps.

    TLDR -- Instead of trying to override ==, you provide a custom comparing method, make == call it, and override the custom comparing method if needed.


    So you said

    All of the classes will only be used in Swift so I do not want to involve NSObject or the NSCopying protocol.

    But if you were to subclass NSObject, how will you write your custom comparison method? You will override isEqual(Any?), right? And if you try to conform to Equatable protocol in your subclass, compiler will complain about "Redundant conformance to protocol Equatable" because NSObject already conformed to Equatable.

    Now that gives us some hints about how NSObject handles this problem -- it provides a custom comparing method isEqual(Any?), call it inside ==, and its subclasses can override it if needed. You can do the same in your own base class.

    Without further ado, let's do some experiments(in Swift 4). Define some classes

    class Grandpa: Equatable {
        var x = 0
    
        static func ==(lhs: Grandpa, rhs: Grandpa) -> Bool {
            return lhs.isEqual(to: rhs)
        }
    
        func isEqual(to object: Any?) -> Bool {
            guard object != nil && type(of: object!) == Grandpa.self else {
                return false
            }
            let value = object as! Grandpa
            return x == value.x
        }
    }
    
    class Father: Grandpa {
        var y = 0
    
        override func isEqual(to object: Any?) -> Bool {
            guard object != nil && type(of: object!) == Father.self else {
                return false
            }
            let value = object as! Father
            return x == value.x && y == value.y
        }
    }
    
    class Son: Father {
        var z = 0
    
        override func isEqual(to object: Any?) -> Bool {
            guard object != nil && type(of: object!) == Son.self else {
                return false
            }
            let value = object as! Son
            return x == value.x && y == value.y && z == value.z
        }
    }
    

    And write some test code

    let grandpa1 = Grandpa()
    let grandpa2 = Grandpa()
    let grandpa3: Grandpa? = nil
    let grandpa4: Grandpa? = nil
    let father1 = Father()
    let father2 = Father()
    let father3 = Father()
    father3.y = 1
    let son1 = Son()
    let son2 = Son()
    let son3 = Son()
    son3.z = 1
    
    print("grandpa1 == grandpa2: \(grandpa1 == grandpa2)")
    print("grandpa1 == grandpa3: \(grandpa1 == grandpa3)")
    print("grandpa3 == grandpa4: \(grandpa3 == grandpa4)")
    print("grandpa1 == father1: \(grandpa1 == father1)")
    print("father1 == father2: \(father1 == father2)")
    print("father1 == father3: \(father1 == father3)")
    print("son1 == son2: \(son1 == son2)")
    print("son1 == son3: \(son1 == son3)")
    

    Run it and you should get

    grandpa1 == grandpa2: true
    grandpa1 == grandpa3: false
    grandpa3 == grandpa4: true
    grandpa1 == father1: false
    father1 == father2: true
    father1 == father3: false
    son1 == son2: true
    son1 == son3: false
    

提交回复
热议问题