2016-10-07 50 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