2016-10-07 107 views
10

我试图在基类中实现==运算符(从Equatable),并且它的子类在Swift 3中。所有类将只在Swift中使用,所以我不想涉及NSObjectNSCopying协议。如何在类层次结构中正确实现Equatable协议?

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

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

到目前为止这么好。现在的子类:

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改变staticclass

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'

唉。这远比它应该更复杂。

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

回答

8

经过大量的研究和一些试验和错误,我终于想出了一个工作解决方案。第一步是将==运算符从类中移动到全局范围。这修复了关于staticfinal的错误。

基类这成为:

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

class Base : Equatable { 
    var x : Int 
} 

而对于子类:

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语句会导致在基类中的==运营商的呼叫。


最终溶液:

基地。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 
} 
+1

哇。巧妙的解决方法,但这真的是Swift让我们做的吗? –

0

我知道它已经有一段时间,因为这个问题被贴了,但我希望我的回答可以帮助。

TLDR - 不要试图覆盖==,而是提供自定义比较方法,使==可以调用它,并根据需要覆盖自定义比较方法。


所以你说

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

但如果你子类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 
    } 
} 

编写一些测试代码

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 
+0

1.你在子类中实现'isEqual'应该在检查子类的属性后调用'super.isEqual'。一个子类不应该检查其父类的任何属性。 2.没有真正与问题有关,但你的GrandPa,父亲,儿子阶级层次是倒退的。从逻辑上讲,儿子不是父亲,父亲也不是大爷。 Son类应该是根类。父亲应该延长儿子,而且爷爷应该延长父亲。 – rmaddy