Equatableプロトコルと継承について

###この記事の環境
- XCode 9.4.1
- Swift 4.1

###この記事の対象者
- Equatableプロトコルを実装したクラスを作って、さらにサブクラス（継承）を作ると挙動がおかしいぞ、と思って困っている人

###問題のある実装

Main0はnameプロパティを持っていてEquatableプロトコルをnameプロパティを使って実装しています。
これは特に問題がないです。

```swift

open class Main0: Equatable
{
    public private(set) var name: String
    
    public init(_ name: String) {
        self.name = name
    }

    // Equatable
    static public func ==(lhs: Main0, rhs: Main0) -> Bool {
        return lhs.name == rhs.name
    }
}

```

問題は次のMain0を継承した下のSub0クラスです。
Sub0クラスはdetailプロパティも持っているので２つを比較して一致すれば同一のEqualだと判断します。しかしながらこちらのEquatableプロトコル実装（つまり==）は呼び出されません。

```swift

// ダメな継承
open class Sub0: Main0
{
    public private(set) var detail: String
    
    public init(_ name: String, detail: String) {
        self.detail = detail
        super.init(name)
    }
    
    // Equatable
    static public func ==(lhs: Sub0, rhs: Sub0) -> Bool {
        // 呼ばれない
        return (lhs.name == rhs.name) && (lhs.detail == rhs.detail)
    }
}

```

これはプロトコルの実装を継承した場合の制約になります。
ではどのようにすればSub0で==が正しく使えるようになるでしょうか。

###期待通りの動作をする実装

下記のMain1クラスのように、func ==()に直接比較処理を記述するのではなく、equal()関数を実装してそれを呼び出します。

```swift

open class Main1: Equatable
{
    public private(set) var name: String
    
    public init(_ name: String) {
        self.name = name
    }
    
    internal func equal(to another: Main1) -> Bool {
        return self.name == another.name
    }
    
    // Equatable
    static public func ==(lhs: Main1, rhs: Main1) -> Bool {
        return lhs.equal(to: rhs)
    }
}

```

Main1クラスを継承したSub1クラスではfunc ==()は必要ありません（呼び出されないので）
代わりにMain1クラスのequal()関数をoverrideします。overrideするために引数がMain1クラスである必要があるのでこのままではdetailプロパティの比較ができません。
そこで下記の例では一度Main1で受けた後にSub1クラスへのCastを試みています。

```swift

open class Sub1: Main1
{
    public private(set) var detail: String
    
    public init(_ name: String, detail: String) {
        self.detail = detail
        super.init(name)
    }
    
    public override func equal(to another: Main1) -> Bool {
        if let b = another as? Sub1 {
            return super.equal(to: another) && (self.detail == b.detail)
        }
        return super.equal(to: another) // ここ微妙
    }
}

```

Castが成功すればdetailプロパティにアクセスできますので、superクラスのequal()関数とANDを取ります。
考えどこは渡されたto another: Main1がSub1でなかった場合です。
falseを返すか、super.equal(to: another)を呼び出すか、これはどのような比較をしたいのかによるのではないでしょうか。

さらにEquatableをProtocol Extensionで実装し、サブクラスを作成した場合の挙動は下記のようになります。下記のNameGettableは、nameプロパティを持っており、かつ==で比較できることがprotocol extensionによって実装されます。

```swift:Swift
// ユニークな識別子を返す
public protocol NameGettable: Equatable
{
    var name: String { get }
}

extension NameGettable
{
    // Equatable
    static public func ==(lhs: Self, rhs: Self) -> Bool
    {
        return lhs.name == rhs.name
    }
}
```

このような場合に同じくfunc ==()を実装してみます。

```swift:Swift
open class Main2: NameGettable
{
    public private(set) var name: String
    
    public init(_ name: String)
    {
        self.name = name
    }
    
    internal func equal(to another: Main2) -> Bool {
        return self.name == another.name
    }
    
    // Equatable
    static public func ==(lhs: Main2, rhs: Main2) -> Bool
    {
        return lhs.equal(to: rhs)
    }
}

open class Sub2: Main2
{
    public private(set) var detail: String
    
    public init(_ name: String, detail: String)
    {
        self.detail = detail
        super.init(name)
    }
    
    public override func equal(to another: Main2) -> Bool {
        if let b = another as? Sub2
        {
            return super.equal(to: another) && (self.detail == b.detail)
        }
        return super.equal(to: another) // ここ微妙
    }
}
```

結論は、Main1、Sub1と同様になり、extension NameGettableは上書きされた形になります。
これを用いれば通常はextension NameGettableで挙動を定義しながら、継承などの場合を考慮したクラスは独自のfunc ==()を実装することができることになります。

下記にTestコードを記載します。

```swift:Swift

class EquatableTests: XCTestCase {
    
    // =============================================================================
    // MARK: - 1
    // =============================================================================

    func testMain0Equality() {
        
        let a = Main0("123")
        let b = Main0("123")
        
        XCTAssertEqual(a, b)
    }
    
    func testMain0NotEquality() {
        
        let a = Main0("123")
        let b = Main0("456")
        
        XCTAssertNotEqual(a, b)
    }
    
    func testSub0Equality() {
        
        let a = Sub0("123", detail: "456")
        let b = Sub0("123", detail: "456")
        
        XCTAssertEqual(a, b)
    }
    
    func testSub0NotEquality() {
        
        let a = Sub0("123", detail: "456")
        let b = Sub0("123", detail: "789")
        
        //XCTAssertNotEqual(a, b) // XCTAssertNotEqual failed: ("Sub0") is equal to ("Sub0") -
        XCTAssertEqual(a, b)
    }
    
    // =============================================================================
    // MARK: - 2
    // =============================================================================
    
    func testMain1Equality() {
        
        let a = Main1("123")
        let b = Main1("123")
        
        XCTAssertEqual(a, b)
    }
    
    func testMain1NotEquality() {
        
        let a = Main1("123")
        let b = Main1("456")
        
        XCTAssertNotEqual(a, b)
    }
    
    func testSub1Equality() {
        
        let a = Sub1("123", detail: "456")
        let b = Sub1("123", detail: "456")
        
        XCTAssertEqual(a, b)
    }
    
    func testSub1NotEquality() {
        
        let a = Sub1("123", detail: "456")
        let b = Sub1("123", detail: "789")
        
        XCTAssertNotEqual(a, b) // OK
    }
    
    func testMain1Sub1Equality() {
        
        let a = Main1("123")
        let b = Sub1("123", detail: "456")
        
        XCTAssertEqual(a, b)
    }
    
    func testMain1Sub1Equality2() {
        
        let a = Sub1("123", detail: "456")
        let b = Main1("123")
        
        XCTAssertEqual(a, b)
    }
    
    // =============================================================================
    // MARK: - 3
    // =============================================================================
    
    func testMain2Equality() {
        
        let a = Main2("123")
        let b = Main2("123")
        
        XCTAssertEqual(a, b)
    }
    
    func testMain2NotEquality() {
        
        let a = Main2("123")
        let b = Main2("456")
        
        XCTAssertNotEqual(a, b)
    }
    
    func testSub2Equality() {
        
        let a = Sub2("123", detail: "456")
        let b = Sub2("123", detail: "456")
        
        XCTAssertEqual(a, b)
    }
    
    func testSub2NotEquality() {
        
        let a = Sub2("123", detail: "456")
        let b = Sub2("123", detail: "789")
        
        XCTAssertNotEqual(a, b) // OK
    }
    
    func testMain2Sub2Equality() {
        
        let a = Main2("123")
        let b = Sub2("123", detail: "456")
        
        XCTAssertEqual(a, b)
    }
    
    func testMain2Sub2Equality2() {
        
        let a = Sub2("123", detail: "456")
        let b = Main2("123")
        
        XCTAssertEqual(a, b)
    }
}

```