如何在 class 层次结构中正确实施 Equatable 协议?
How to properly implement the Equatable protocol in a class hierarchy?
我正在尝试在基础 class 中实现 ==
运算符(来自 Equatable
)及其在 Swift 中的子 classes 3 . 所有 classes 只会在 Swift 中使用,所以我不想涉及 NSObject
或 NSCopying
协议。
我从一个基础 class 和一个子class 开始:
class Base {
var x : Int
}
class Subclass : Base {
var y : String
}
现在我想将 Equatable
和 ==
运算符添加到 Base
。看起来很简单。从文档中复制 ==
操作员签名:
class Base : Equatable {
var x : Int
static func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
到目前为止一切顺利。现在为 subclass:
class Subclass : Base {
static override func == (lhs: Base, rhs: Base) -> Bool {
return true
}
}
但这会导致错误:
Operator function overrides a 'final' operator function
好的。经过一些研究(仍在学习 Swift 3),我了解到 static
可以替换为 class
以指示类型方法可以被覆盖。
所以我尝试在 Base
中将 static
更改为 class
:
class Base : Equatable {
var x : Int
class func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
但这会导致新的错误:
Operator '==' declared in non-final class 'Base' must be 'final'
呃。这比它应该的要复杂得多。
如何在基 class 和子 class 中正确实现 Equatable
协议和 ==
运算符?
经过大量研究和反复试验,我终于想出了一个可行的解决方案。第一步是将 ==
运算符从 class 内部移动到全局范围。这修复了关于 static
和 final
.
的错误
对于基础 class 这变成了:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
而子class:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
return true
}
class Subclass : Base {
var y : String
}
现在剩下的唯一部分就是弄清楚如何从子class 的==
运算符调用基class 的==
运算符。这让我找到了最终的解决方案:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
if lhs.y == rhs.y {
if lhs as Base == rhs as Base {
return true
}
}
return false
}
第一个 if
语句导致调用基 class.
中的 ==
运算符
最终解决方案:
Base.swift:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
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
}
我知道问题发布已经有一段时间了,但我希望我的回答对您有所帮助。
TLDR -- 与其尝试覆盖 ==
,不如提供自定义比较方法,让 ==
调用它,并在需要时覆盖自定义比较方法。
所以你说
All of the classes will only be used in Swift so I do not want to involve NSObject
or the NSCopying
protocol.
但是如果你是 subclass NSObject
,你将如何编写自定义比较方法?您将覆盖 isEqual(Any?)
,对吗?如果你试图在你的 subclass 中遵守 Equatable
协议,编译器会抱怨 "Redundant conformance to protocol Equatable
" 因为 NSObject
已经遵守 Equatable
。
现在给了我们一些关于 NSObject
如何处理这个问题的提示 -- 它提供了一个自定义比较方法 isEqual(Any?)
,在 ==
中调用它,它的子 [=47] =]es 可以根据需要覆盖它。你可以在你自己的基地做同样的事情 class.
事不宜迟,让我们做一些实验(在 Swift 4 中)。定义一些 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
}
}
并写一些测试代码
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)")
运行 它,你应该得到
grandpa1 == grandpa2: true
grandpa1 == grandpa3: false
grandpa3 == grandpa4: true
grandpa1 == father1: false
father1 == father2: true
father1 == father3: false
son1 == son2: true
son1 == son3: false
继 之后,我想出了一个检测相等性的守卫方法:
Base.swift
static func ==(lhs: Base, rhs: Base) -> Bool {
// ensure class properties match
guard lhs.x == rhs.x else {
return false
}
return true
}
Subclass.swift
static func ==(lhs: Subclass, rhs: Subclass) -> Bool {
// ensure base class properties match
guard lhs as Base == rhs as Base else {
return false
}
// ensure class properties match
guard lhs.y == rhs.y else {
return false
}
return true
}
```
根据其他答案,我想到了这个:
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)
}
}
我正在尝试在基础 class 中实现 ==
运算符(来自 Equatable
)及其在 Swift 中的子 classes 3 . 所有 classes 只会在 Swift 中使用,所以我不想涉及 NSObject
或 NSCopying
协议。
我从一个基础 class 和一个子class 开始:
class Base {
var x : Int
}
class Subclass : Base {
var y : String
}
现在我想将 Equatable
和 ==
运算符添加到 Base
。看起来很简单。从文档中复制 ==
操作员签名:
class Base : Equatable {
var x : Int
static func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
到目前为止一切顺利。现在为 subclass:
class Subclass : Base {
static override func == (lhs: Base, rhs: Base) -> Bool {
return true
}
}
但这会导致错误:
Operator function overrides a 'final' operator function
好的。经过一些研究(仍在学习 Swift 3),我了解到 static
可以替换为 class
以指示类型方法可以被覆盖。
所以我尝试在 Base
中将 static
更改为 class
:
class Base : Equatable {
var x : Int
class func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
但这会导致新的错误:
Operator '==' declared in non-final class 'Base' must be 'final'
呃。这比它应该的要复杂得多。
如何在基 class 和子 class 中正确实现 Equatable
协议和 ==
运算符?
经过大量研究和反复试验,我终于想出了一个可行的解决方案。第一步是将 ==
运算符从 class 内部移动到全局范围。这修复了关于 static
和 final
.
对于基础 class 这变成了:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
而子class:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
return true
}
class Subclass : Base {
var y : String
}
现在剩下的唯一部分就是弄清楚如何从子class 的==
运算符调用基class 的==
运算符。这让我找到了最终的解决方案:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
if lhs.y == rhs.y {
if lhs as Base == rhs as Base {
return true
}
}
return false
}
第一个 if
语句导致调用基 class.
==
运算符
最终解决方案:
Base.swift:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
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
}
我知道问题发布已经有一段时间了,但我希望我的回答对您有所帮助。
TLDR -- 与其尝试覆盖 ==
,不如提供自定义比较方法,让 ==
调用它,并在需要时覆盖自定义比较方法。
所以你说
All of the classes will only be used in Swift so I do not want to involve
NSObject
or theNSCopying
protocol.
但是如果你是 subclass NSObject
,你将如何编写自定义比较方法?您将覆盖 isEqual(Any?)
,对吗?如果你试图在你的 subclass 中遵守 Equatable
协议,编译器会抱怨 "Redundant conformance to protocol Equatable
" 因为 NSObject
已经遵守 Equatable
。
现在给了我们一些关于 NSObject
如何处理这个问题的提示 -- 它提供了一个自定义比较方法 isEqual(Any?)
,在 ==
中调用它,它的子 [=47] =]es 可以根据需要覆盖它。你可以在你自己的基地做同样的事情 class.
事不宜迟,让我们做一些实验(在 Swift 4 中)。定义一些 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
}
}
并写一些测试代码
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)")
运行 它,你应该得到
grandpa1 == grandpa2: true
grandpa1 == grandpa3: false
grandpa3 == grandpa4: true
grandpa1 == father1: false
father1 == father2: true
father1 == father3: false
son1 == son2: true
son1 == son3: false
继
Base.swift
static func ==(lhs: Base, rhs: Base) -> Bool {
// ensure class properties match
guard lhs.x == rhs.x else {
return false
}
return true
}
Subclass.swift
static func ==(lhs: Subclass, rhs: Subclass) -> Bool {
// ensure base class properties match
guard lhs as Base == rhs as Base else {
return false
}
// ensure class properties match
guard lhs.y == rhs.y else {
return false
}
return true
}
```
根据其他答案,我想到了这个:
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)
}
}