SwiftでEquatableをマスターするための8選サンプルコード

SwiftのEquatableプロトコルを学ぶ人のイラストSwift
この記事は約18分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

SwiftはAppleが開発したプログラミング言語であり、iOSやmacOSなどのAppleの製品を開発する際の主要な言語として使用されています。

この言語は、簡潔さと安全性を兼ね備えた言語設計がなされており、初心者から経験者まで幅広く利用されています。

今回は、Swiftの中でも「Equatableプロトコル」という特定の部分に焦点を当て、その使い方や実用的なサンプルコード、さらには注意点やカスタマイズ方法まで、初心者にも分かりやすく解説していきます。

Equatableはオブジェクトの等価性を判断するためのプロトコルで、Swiftのコーディングにおいて頻繁に使用されるため、このプロトコルをしっかりと理解することで、より質の高いコードを書く手助けとなるでしょう。

●SwiftとEquatableプロトコルの基本

Swiftは、高性能で安全性に優れたモダンなプログラミング言語として知られています。

特にAppleのエコシステム内でのアプリケーション開発に欠かせない存在となっています。

一方、EquatableプロトコルはSwiftの重要な部分を成すもので、オブジェクト同士の比較を可能にするキーとなるプロトコルです。

○Swiftとは

Swiftは、Appleが2014年に発表した新しいプログラム言語です。

Objective-Cに代わって、iOSやmacOSなどのApple製品の開発で広く利用されている言語となりました。

Swiftの特徴としては、読みやすく書きやすいシンタックス、強力な型システム、そして高速な実行速度が挙げられます。

また、Playgroundという機能を使えば、リアルタイムでコードの実行結果を確認しながらプログラムを作成することができるので、初学者にも非常に学びやすい言語となっています。

○Equatableプロトコルの概要

Equatableプロトコルは、Swiftにおいてオブジェクト間の等価性を判断するためのプロトコルです。

具体的には、2つのインスタンスが等しいかどうかを==オペレーターを使用して判断することができます。

多くの基本的なデータ型、例えばStringやIntなどは、Swiftの標準ライブラリにおいてすでにEquatableプロトコルを採用しているため、特別な実装をせずともそのまま比較することができます。

しかし、自分で定義した構造体やクラスに対して、その等価性を定義したい場合は、Equatableプロトコルを適用し、==オペレーターの動作を明示的に定義する必要があります。

これにより、独自のデータ型やオブジェクトにおいても、簡潔に等価性の判断を行うことができるようになります。

●Equatableの使い方

Swiftでは、オブジェクトやデータ型同士の等価性を判断するための機能としてEquatableプロトコルが提供されています。

これは、オブジェクトが等しいかどうか、すなわち同じ値を持つかどうかを確認するためのプロトコルです。

基本的なデータ型(例:Int、String)では、このプロトコルがすでに採用されているため、特別な実装をせずともそのまま比較することができます。

しかしながら、自身で定義したクラスや構造体に対しては、この等価性を明示的に定義することが求められます。

ここでは、その使い方について2つのサンプルコードをもとに解説していきます。

○サンプルコード1:基本的なEquatableの実装

こちらのコードでは、Personという構造体を定義し、Equatableプロトコルを採用しています。

この例では、nameとageの2つのプロパティを持つPersonを等価と判断するための方法を表しています。

struct Person: Equatable {
    let name: String
    let age: Int

    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
    }
}

let person1 = Person(name: "山田太郎", age: 25)
let person2 = Person(name: "山田太郎", age: 25)
let person3 = Person(name: "佐藤花子", age: 30)

if person1 == person2 {
    print("person1とperson2は同じです。")
} else {
    print("person1とperson2は異なります。")
}

このコードでは、==オペレーターを使用して2つのPersonインスタンスが等しいかどうかを判断することができます。

具体的には、nameプロパティとageプロパティの両方が一致した場合に、2つのインスタンスは等しいとみなされます。

実際にこのコードを実行すると、”person1とperson2は同じです。”というメッセージが表示されます。

なぜなら、person1とperson2は同じnameとageを持っているためです。

○サンプルコード2:カスタムクラスでのEquatableの利用

次に、カスタムクラスを使用したEquatableの利用例を見てみましょう。

この例では、Carクラスを定義し、それをEquatableプロトコルに準拠させています。

class Car: Equatable {
    let brand: String
    let model: String

    init(brand: String, model: String) {
        self.brand = brand
        self.model = model
    }

    static func ==(lhs: Car, rhs: Car) -> Bool {
        return lhs.brand == rhs.brand && lhs.model == rhs.model
    }
}

let car1 = Car(brand: "Toyota", model: "Corolla")
let car2 = Car(brand: "Toyota", model: "Corolla")
let car3 = Car(brand: "Honda", model: "Civic")

if car1 == car2 {
    print("car1とcar2は同じモデルです。")
} else {
    print("car1とcar2は異なるモデルです。")
}

このコードでは、==オペレーターをオーバーロードして、2つのCarインスタンスが等しいかどうかを判断します。

こちらも、brandプロパティとmodelプロパティの両方が一致した場合に、2つのインスタンスは等しいとみなされます。

このコードを実行すると、”car1とcar2は同じモデルです。”というメッセージが表示されます。

これは、car1とcar2が同じbrandとmodelの値を持っているためです。

○サンプルコード3:配列内の要素の比較

Swiftでの配列の要素の比較には、Equatableプロトコルが使用されます。

配列が持っている各要素を一つずつ比較し、すべての要素が等しい場合に限り、2つの配列は等しいとみなされます。

このコードでは、Equatableプロトコルを使って、2つの配列が等しいかどうかを比較する方法を示しています。

この例では、Int型の要素を持つ2つの配列を作成し、それらが等しいかどうかを比較しています。

let array1 = [1, 2, 3]
let array2 = [1, 2, 3]
let array3 = [1, 2, 4]

let isEqual12 = array1 == array2
let isEqual13 = array1 == array3

上のコードでは、array1array2は同じ要素を持つので、isEqual12trueと評価されます。

一方、array1array3は最後の要素が異なるため、isEqual13falseと評価されます。

このようにして、Swiftの配列内の要素を効率的に比較することができます。

実行結果として、isEqual12trueisEqual13falseとなります。

○サンプルコード4:辞書のキーとしての利用

Swiftの辞書では、キーの比較にEquatableプロトコルが使われます。

辞書のキーとして使用する型がEquatableプロトコルに準拠していれば、そのキーを持つ辞書の要素にアクセスすることができます。

このコードでは、Equatableプロトコルを準拠したカスタム型を辞書のキーとして使用する方法を表しています。

この例では、名前を持つ簡単な構造体を作成し、それをキーとする辞書を定義しています。

struct Person: Equatable {
    let name: String
}

let john = Person(name: "John")
let dictionary: [Person: String] = [john: "Engineer"]

if let occupation = dictionary[john] {
    print("John's occupation is \(occupation)")
}

上のコードでは、Person構造体がEquatableプロトコルに準拠しているため、johnをキーとしてdictionaryから値を取得することができます。

実行結果として、John's occupation is Engineerという文字列が出力されます。

●Equatableの応用例

SwiftでEquatableプロトコルを使用するときの応用例をいくつか紹介します。

Equatableは非常に柔軟で、多くの場面で活用することができます。

ここでは、その多様な利用シーンをサンプルコードを交えて解説します。

○サンプルコード5:多重継承とEquatable

Swiftでは、クラスの多重継承はサポートされていませんが、複数のプロトコルを採用することができます。

ここでは、Equatableを他のプロトコルと併用する方法を取り上げます。

protocol Describable {
    var description: String { get }
}

struct Person: Describable, Equatable {
    let name: String
    let age: Int

    var description: String {
        return "\(name)は\(age)歳です。"
    }

    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
    }
}

let person1 = Person(name: "太郎", age: 25)
let person2 = Person(name: "太郎", age: 25)

if person1 == person2 {
    print("同じPersonです。")
} else {
    print("異なるPersonです。")
}

このコードでは、Describableという新しいプロトコルを定義しています。

そして、Person構造体はこのDescribableプロトコルとEquatableプロトコルの両方を採用しています。

この例では、Person型のインスタンスを比較する際に名前と年齢の両方が同じ場合に等しいと判断しています。

実際にこのコードを実行すると、”同じPersonです。”と表示されます。

○サンプルコード6:構造体の中の構造体での比較

複雑なデータ構造を持つ場合、構造体内に別の構造体が含まれることがあります。

このような場合でもEquatableを使用して簡単に比較が可能です。

struct Address: Equatable {
    let city: String
    let street: String
}

struct User: Equatable {
    let name: String
    let address: Address
}

let address1 = Address(city: "東京", street: "青山")
let address2 = Address(city: "東京", street: "青山")

let user1 = User(name: "太郎", address: address1)
let user2 = User(name: "太郎", address: address2)

if user1 == user2 {
    print("同じUserです。")
} else {
    print("異なるUserです。")
}

このコードでは、Addressという構造体が定義され、その中のcitystreetを基に等価性を判断します。

その上でUserという構造体も定義しており、このUserAddressをプロパティとして持ちます。

Userのインスタンスを比較する際には、内部のAddressの情報も基に等価性が判断されます。

このコードを実行すると、”同じUserです。”と表示されることが確認できます。

○サンプルコード7:プロパティの一部のみを比較する方法

Equatableプロトコルを実装する際、オブジェクトの全プロパティを比較するのではなく、特定のプロパティだけを比較したい場合があります。

例えば、ユーザークラスが名前やメールアドレスなどの複数のプロパティを持つ場合、名前だけを基に等価性を判断したいというケースが考えられます。

下記のコードは、Userクラスを定義し、名前のみを比較して等価性を判断する方法を表しています。

// Userクラスの定義
class User: Equatable {
    var name: String
    var email: String

    init(name: String, email: String) {
        self.name = name
        self.email = email
    }

    // Equatableプロトコルの実装
    static func == (lhs: User, rhs: User) -> Bool {
        return lhs.name == rhs.name
    }
}

// インスタンスの作成と比較
let user1 = User(name: "山田太郎", email: "taro@example.com")
let user2 = User(name: "山田太郎", email: "taro2@example.com")

if user1 == user2 {
    print("2人のユーザーは名前が同じです。")
} else {
    print("2人のユーザーは名前が異なります。")
}

このコードでは、Userクラスを使って、名前が山田太郎の2つのインスタンスを作成しています。

メールアドレスは異なるものの、名前が同じなので、比較の結果として2人のユーザーは名前が同じです。という出力が得られます。

○サンプルコード8:特定の条件下でのみ等価と判断する方法

場合によっては、特定の条件下でのみ等価と判断したい場合があります。

例えば、商品の在庫状況を表すItemクラスがあり、在庫が10個以下の場合のみ等価と判断したいとします。

下記のコードは、そのような条件を実装したサンプルを表しています。

// Itemクラスの定義
class Item: Equatable {
    var name: String
    var stock: Int

    init(name: String, stock: Int) {
        self.name = name
        self.stock = stock
    }

    // Equatableプロトコルの実装
    static func == (lhs: Item, rhs: Item) -> Bool {
        if lhs.stock <= 10 && rhs.stock <= 10 {
            return true
        }
        return lhs.name == rhs.name && lhs.stock == rhs.stock
    }
}

// インスタンスの作成と比較
let item1 = Item(name: "ペン", stock: 5)
let item2 = Item(name: "ノート", stock: 7)

if item1 == item2 {
    print("在庫が10個以下のため、2つのアイテムは等価と判断されます。")
} else {
    print("アイテムが異なるため、等価ではありません。")
}

このコードでは、Itemクラスの2つのインスタンスを作成して比較しています。

名前や在庫数は異なるものの、在庫が10個以下のため、等価と判断され在庫が10個以下のため、2つのアイテムは等価と判断されます。という出力が得られます。

●注意点と対処法

SwiftのEquatableプロトコルを使うときの注意点とその対処法について詳しく見ていきましょう。

○Equatableの自動導出の限界

Swiftでは、Equatableプロトコルを簡単に採用することで、==オペレーターを使ってオブジェクト間の等価性をチェックすることができます。

しかし、この自動導出にはいくつかの限界があります。

このコードでは、基本的な構造体を表しています。

この例では、全てのプロパティがEquatableプロトコルを満たしているため、自動導出が可能です。

struct SimpleStruct: Equatable {
    var name: String
    var age: Int
}

このSimpleStructは、Swiftが自動的に==オペレータを生成してくれます。

しかし、全てのケースでこの自動導出がうまくいくわけではありません。

例えば、関数やクロージャを持つ構造体やクラスでは、自動導出はサポートされていません。

○複雑なオブジェクト比較の注意点

Equatableプロトコルは非常に便利ですが、複雑なオブジェクトの比較を行う際には注意が必要です。

例えば、次のようなクラスがあるとします。

class ComplexClass {
    var name: String
    var age: Int
    var friends: [ComplexClass]

    init(name: String, age: Int, friends: [ComplexClass] = []) {
        self.name = name
        self.age = age
        self.friends = friends
    }
}

この例では、ComplexClassは自分自身のリストを持っています。

このような循環参照を持つクラスを比較する場合、単純に==を使用すると無限ループに陥る可能性があります。

従って、このような複雑なオブジェクトを比較する場合、自分で==オペレータを実装することが推奨されます。

オブジェクトの内容を正確に、そして安全に比較するためのロジックを慎重に設計する必要があります。

●カスタマイズ方法

EquatableプロトコルはSwiftの核となる部分の1つで、オブジェクトの等価性をチェックするために使用されます。

ここでは、Equatableの基本的な使い方から一歩進んで、カスタマイズの方法について掘り下げていきます。

○Equatableを拡張する方法

Swiftでは、既存の型やプロトコルを拡張して、新しい機能やメソッドを追加することができます。Equatableも例外ではありません。

Equatableを拡張することで、独自の比較ロジックを追加することができます。

例として、Int型を拡張して、ある値との差が10以内であれば等しいとみなすような比較を追加してみましょう。

extension Int: Equatable {
    static func ~=(lhs: Int, rhs: Int) -> Bool {
        return abs(lhs - rhs) <= 10
    }
}

let a = 15
let b = 7
if a ~= b {
    print("aとbは10以内の差です。")
}

このコードでは、Int型を拡張して新しい~=オペレーターを追加しています。

この例では、abの差が10以内であれば、”aとbは10以内の差です。”と出力されます。

○カスタムオペレーターの活用

Swiftでは、独自のオペレータを定義することもできます。

これを利用して、Equatableのカスタム比較をより柔軟に行うことができます。

例えば、文字列の前方一致を判定するカスタムオペレータを作成してみましょう。

infix operator ^^
extension String {
    static func ^^(lhs: String, rhs: String) -> Bool {
        return lhs.hasPrefix(rhs)
    }
}

let string1 = "HelloWorld"
let prefix1 = "Hello"
if string1 ^^ prefix1 {
    print("string1はprefix1で始まります。")
}

このコードでは、^^というオペレーターを定義し、String型を拡張してこのオペレーターを使って前方一致を判定する機能を追加しています。

この例では、string1prefix1で始まる場合、”string1はprefix1で始まります。”と出力されます。

まとめ

SwiftのEquatableプロトコルは、オブジェクト間の等価性を確認するための中心的な役割を果たしています。

この記事を通じて、SwiftとEquatableの基本から、さまざまな応用例や注意点、さらにはカスタマイズ方法までを学ぶことができたかと思います。

特に、Swiftの強力な型拡張機能を利用することで、Equatableをさらに強化し、柔軟な比較ロジックを独自に実装する方法が紹介されました。

Equatableは、Swiftでのプログラミングにおいて頻繁に使用されるプロトコルです。

そのため、これを適切に使用し、必要に応じてカスタマイズすることで、より効果的なコードを書くことができます。

今後Swiftを使用する際には、この記事で学んだEquatableの知識を活用し、より質の高いコードの実装を目指してください。

また、常に新しい情報や技術を追い求めることで、Swiftの深い部分まで理解し、その全ての機能を最大限に利用することができるでしょう。