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

Swift 如何在类层次结构中正确实现equalable协议?,swift,subclass,swift3,equatable,Swift,Subclass,Swift3,Equatable,我试图在Swift 3的基类及其子类中实现=操作符(来自equalable)。所有的类将只在Swift中使用,因此我不想涉及NSObject或NSCopying协议 我从一个基类和一个子类开始: class Base { var x : Int } class Subclass : Base { var y : String } class Subclass : Base { static override func == (lhs: Base, rhs: Base)

我试图在Swift 3的基类及其子类中实现
=
操作符(来自
equalable
)。所有的类将只在Swift中使用,因此我不想涉及
NSObject
NSCopying
协议

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

class Base {
    var x : Int
}

class Subclass : Base {
    var y : String
}
class Subclass : Base {
    static override func == (lhs: Base, rhs: Base) -> Bool {
        return true
    }
}
func == (lhs: Subclass, rhs: Subclass) -> Bool {
    return true
}

class Subclass : Base {
    var y : String
}
现在我想将
equalable
=
操作符添加到
Base
。看起来很简单。从文档中复制操作员签名:

class Base : Equatable {
    var x : Int

    static func == (lhs: Base, rhs: Base) -> Bool {
        return lhs.x == rhs.x
    }
}
到目前为止还不错。现在,对于子类:

class Base {
    var x : Int
}

class Subclass : Base {
    var y : String
}
class Subclass : Base {
    static override func == (lhs: Base, rhs: Base) -> Bool {
        return true
    }
}
func == (lhs: Subclass, rhs: Subclass) -> Bool {
    return true
}

class Subclass : Base {
    var y : String
}
但这会导致一个错误:

操作员功能覆盖“最终”操作员功能

嗯。经过一些研究(仍在学习Swift 3),我了解到可以用
class
替换
static
,以指示可以重写type方法

因此,我尝试在
Base
中将
static
更改为
class

class Base : Equatable {
    var x : Int

    class func == (lhs: Base, rhs: Base) -> Bool {
        return lhs.x == rhs.x
    }
}
但这会导致一个新的错误:

在非final类“Base”中声明的运算符“==”必须为“final”

啊。这远比实际情况复杂得多


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

经过大量研究和反复尝试,我终于找到了一个可行的解决方案。第一步是将
=
操作符从类内部移动到全局范围。这修复了有关
静态
最终
的错误

对于基类,这成为:

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

class Base : Equatable {
    var x : Int
}
对于子类:

class Base {
    var x : Int
}

class Subclass : Base {
    var y : String
}
class Subclass : Base {
    static override func == (lhs: Base, rhs: Base) -> Bool {
        return true
    }
}
func == (lhs: Subclass, rhs: Subclass) -> Bool {
    return true
}

class Subclass : Base {
    var y : String
}
现在剩下的唯一部分是弄清楚如何从子类的
=
操作符调用基类的
=
操作符。这让我找到了最终的解决方案:

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

    return false
}
第一个
if
语句导致调用基类中的
=
运算符


最终解决办法:

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

    return false
}
Base.swift:

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

class Base : Equatable {
    var x : Int
}
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—您不需要尝试重写
=
,而是提供一个自定义比较方法,让
==
调用它,并在需要时重写自定义比较方法


你是这么说的

所有的类将只在Swift中使用,因此我不想涉及
NSObject
NSCopying
协议

但是,如果要将
NSObject
子类化,您将如何编写自定义比较方法?您将覆盖isEqual(任何?),对吗?如果您试图在子类中遵守
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
    }
}
并编写一些测试代码

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
接下来,我提出了一种测试平等性的方法:

斯威夫特
斯威夫特亚类

```

以下是我的其他答案:

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

哇!聪明的工作,但这真的是斯威夫特让我们做的吗。子类中
isEqual
的实现应该在只检查子类的属性之后调用
super.isEqual
。子类不应检查其父类的任何属性。2.与这个问题无关,但你的祖父、父亲、儿子的阶级结构是倒退的。从逻辑上讲,儿子不是父亲,父亲也不是祖父。子类应该是根类。父亲应该抚养儿子,爷爷应该抚养父亲。