如何在类层次结构中正确实现Equatable协议?

rma*_*ddy 13 subclass swift equatable swift3

我正在尝试在Swift 3中的基类及其子类中实现==运算符(from Equatable).所有类都只在Swift中使用,因此我不想涉及NSObject或使用NSCopying协议.

我从一个基类和一个子类开始:

class Base {
    var x : Int
}

class Subclass : Base {
    var y : String
}
Run Code Online (Sandbox Code Playgroud)

现在我想添加Equatable==运营商Base.看起来很简单.==从文档中复制操作员签名:

class Base : Equatable {
    var x : Int

    static func == (lhs: Base, rhs: Base) -> Bool {
        return lhs.x == rhs.x
    }
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.现在为子类:

class Subclass : Base {
    static override func == (lhs: Base, rhs: Base) -> Bool {
        return true
    }
}
Run Code Online (Sandbox Code Playgroud)

但这会导致错误:

运算符函数会覆盖"最终"运算符函数

好.经过一些研究(仍在学习Swift 3),我学会了static可以替换class为表示类型方法可以被覆盖.

所以,我试图改变staticclassBase:

class Base : Equatable {
    var x : Int

    class func == (lhs: Base, rhs: Base) -> Bool {
        return lhs.x == rhs.x
    }
}
Run Code Online (Sandbox Code Playgroud)

但这会导致新的错误:

在非最终类'Base'中声明的运算符'=='必须是'final'

啊.这比它应该复杂得多.

如何在基类和子类中正确实现Equatable协议和==运算符?

rma*_*ddy 13

经过大量的研究和一些反复试验,我终于想出了一个有效的解决方案.第一步是将==操作员从类内部移动到全局范围.这修复了关于static和的错误final.

对于基类,这成了:

func == (lhs: Base, rhs: Base) -> Bool {
    return lhs.x == rhs.x
}

class Base : Equatable {
    var x : Int
}
Run Code Online (Sandbox Code Playgroud)

对于子类:

func == (lhs: Subclass, rhs: Subclass) -> Bool {
    return true
}

class Subclass : Base {
    var y : String
}
Run Code Online (Sandbox Code Playgroud)

现在剩下的唯一部分是弄清楚如何====子类的运算符调用基类的运算符.这导致我得出最终的解决方案:

func == (lhs: Subclass, rhs: Subclass) -> Bool {
    if lhs.y == rhs.y {
        if lhs as Base == rhs as Base {
            return true
        }
    }

    return false
}
Run Code Online (Sandbox Code Playgroud)

第一个if语句导致==对基类中的运算符的调用.


最终的解决方案:

Base.swift:

func == (lhs: Base, rhs: Base) -> Bool {
    return lhs.x == rhs.x
}

class Base : Equatable {
    var x : Int
}
Run Code Online (Sandbox Code Playgroud)

Subclass.swift:

func == (lhs: Subclass, rhs: Subclass) -> Bool {
    if lhs.y == rhs.y {
        if lhs as Base == rhs as Base {
            return true
        }
    }

    return false
}

class Subclass : Base {
    var y : String
}
Run Code Online (Sandbox Code Playgroud)

  • 哇。巧妙的解决方法,但这真的是 Swift 让我们做的吗? (3认同)

小智 5

我知道问题发布已经有一段时间了,但我希望我的回答对您有所帮助。

TLDR - 您无需尝试覆盖==,而是提供自定义比较方法,调用==它,并在需要时覆盖自定义比较方法。


所以你说

所有的类都只会在 Swift 中使用,所以我不想涉及NSObject协议NSCopying

但是如果您子类化NSObject,您将如何编写自定义比较方法?你会覆盖isEqual(Any?),对吧?如果您尝试在子类中遵守Equatable协议,编译器会抱怨“协议的冗余一致性Equatable”,因为NSObject已经符合Equatable.

现在这给了我们一些关于如何NSObject处理这个问题的提示——它提供了一个自定义的比较方法isEqual(Any?),在内部调用它==,并且它的子类可以在需要时重写它。您可以在自己的基类中执行相同的操作。

话不多说,让我们做一些实验(在 Swift 4 中)。定义一些类

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
    }
}
Run Code Online (Sandbox Code Playgroud)

并编写一些测试代码

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 Code Online (Sandbox Code Playgroud)

运行它,你应该得到

grandpa1 == grandpa2: true
grandpa1 == grandpa3: false
grandpa3 == grandpa4: true
grandpa1 == father1: false
father1 == father2: true
father1 == father3: false
son1 == son2: true
son1 == son3: false
Run Code Online (Sandbox Code Playgroud)


小智 5

根据其他答案,我想出了这个:

class Base : Equatable {
    var x : Int
    static func == (lhs: Base, rhs: Base) -> Bool {
        return lhs.x == rhs.x
    }
}

class Subclass : Base {
    var y : String
    static func == (lhs: Subclass, rhs: Subclass) -> Bool {
        return lhs.y == rhs.y && (lhs as Base) == (rhs as Base)
    }
}
Run Code Online (Sandbox Code Playgroud)