今回はSwiftのプロトコルについて初心者にも分かりやすいように解説していきたいと思います。
この記事を読むと、
・プロトコルとは
・プロトコルの基本
・標準ライブラリのプロトコル
などを学ぶ事ができます。
ぜひ最後まで読んでいってください。
クリックできる目次
プロトコルとは
プロトコルとは、型のインターフェースを定義するものです。インターフェースは、型がどんなメソッド、プロパティ、イニシャライザ、インデックス、サブスクリプションなどを持っているか定義します。
インターフェースを定義する目的
プロトコルは、型が特定の性質や機能を持つために必要なインターフェースを定義するためのものです。また、プロトコルが要求するインターフェースを型が満たすことを準拠と言います。
プロトコルを利用することで、複数の型で共通となる性質を抽象化できます。例えば、2つの値が同じであるかどうかを同値性といい、同値性が検証可能であるという性質は、標準ライブラリのEquatableプロトコルとして表現されています。Equatableプロトコルには”==演算子”が定義されており、このプロトコルに準拠する型は”==演算子”に対する実装を用意する必要があります。
このようなプロトコルが存在しているおかげで、具体的な型は問わないが、同値性が検証可能な型だけを扱うことが可能になります。例えば次の関数は、2つの引数の値が同じ時だけ、その値を出力しますが、渡せる引数の型をEquatableプロトコルに準拠している型だけに制限しています。Int型もString型もどちらもEquatableプロトコルに準拠しているので、両方の方を引数として渡すことができます。
func printIfEqual<T: Equatable>(arg1: T, arg2: T) {
if arg1 == arg2 {
print("どちらも\(arg1)です")
}
}
printIfEqual(arg1: 12345, arg2: 12345) //どちらも12345です
printIfEqual(arg1: "こんにちは", arg2: "こんにちは") //どちらもこんにちはです
プロトコルの基本
プロトコルの定義方法と、プロトコルへ準拠する方法、そしてプロトコルの利用方法について説明します。
定義方法
プロトコルはprotocolキーワードを使用して宣言し、{}内にプロパティやメソッドなどのプロトコルを構成する要素を定義していきます。
次の例では、Flyableという名前のプロトコルを宣言しています。
protocol Flyable {
func fly()
}
準拠方法
型はプロトコルに準拠することにより、プロトコルで定義されたインターフェースを通じて扱うことができます。
型をプロトコルに準拠させるには、型名の後に「:」を追加し、準拠する対象のプロトコル名を続けます。型は複数のプロトコルに準拠でき、複数のプロトコルに準拠するには、プロトコルを「,」区切りで追加します。次の凡例では、クラスをプロトコルに準拠させています。
class Bird: Flyable {
func fly() {
print("I can fly!")
}
}
プロトコルに準拠するためには、プロトコルが要求している全ての要素を実装している必要があります。例えば、次のFishクラスは、Flyableプロトコルが要求しているfly()メソッドが実装されていないため、コンパイルエラーとなります。
protocol Flyable {
func fly()
}
class Bird: Flyable {
func fly() {
print("I can fly!")
}
}
// fly()が実装されていないのでコンパイルエラー
class Fish: Flyable {
func swim() {
print("I can swim")
}
}
クラス継承時の準拠方法
クラスでは、クラスの継承とプロトコルへの準拠が同じ書式になっています。クラスの継承とプロトコルの準拠を同時に行う場合、継承するスーパークラスを先に書き、続いて準拠するプロトコル名を列挙します。
// 定義したプロトコル
protocol MyProtocol {
var someProperty: Int { get set }
func someMethod() -> String
}
// プロトコルを準拠するクラス
class MyClass: MySuperClass, MyProtocol {
var someProperty: Int = 0
func someMethod() -> String {
return "Hello, Protocols!"
}
}
エクステンションによる準拠方法
プロトコルへの準拠はエクステンションによっても可能です。
以下はその例です。
protocol MyProtocol {
var someProperty: Int { get set }
func someMethod() -> String
}
class MyClass {
var someProperty: Int = 0
}
extension MyClass: MyProtocol {
func someMethod() -> String {
return "Hello, Protocols!"
}
}
1つのエクステンションで複数のプロトコルに準拠することもできますが、1つのプロトコルに対して1つのエクステンションを定義することで、プロパティ、メソッドとプロトコルの対応が明確になります。複数のプロトコルに準拠するときなどは特に、どのプロパティやメソッドがどのプロトコルで宣言されているものなのか分かりづらくなりがちですが、このようにエクステンションを利用すれば、コードの可読性を高めることができます。
標準ライブラリのプロトコル
Swiftの標準ライブラリには、多くのプロトコルが用意されています。以下に、代表的なプロトコルの例をいくつか示します。
Equatableプロトコル
Equatableプロトコルは、2つのオブジェクトが等しいかどうかを比較するためのプロトコルです。以下に、Equatableプロトコルのコード例を示します。
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
上記のプロトコルに準拠するためには、==演算子を実装します。
class MyClass: Equatable {
var x: Int
var y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
static func == (lhs: MyClass, rhs: MyClass) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
}
let a = MyClass(x: 1, y: 2)
let b = MyClass(x: 1, y: 2)
let c = MyClass(x: 2, y: 3)
print(a == b) // true
print(a == c) // false
上記の例では、MyClassクラスがEquatableプロトコルを準拠していることになり、==演算子を実装しているため、a == b, a == cのように比較ができます。
Comparableプロトコル
2つのオブジェクトを比較し、大小関係を確認するためのプロトコルです。以下に、Comparableプロトコルのコード例を示します。
protocol Comparable {
static func < (lhs: Self, rhs: Self) -> Bool
static func <= (lhs: Self, rhs: Self) -> Bool
static func > (lhs: Self, rhs: Self) -> Bool
static func >= (lhs: Self, rhs: Self) -> Bool
}
上記のプロトコルに準拠するためには、<、<=、>、>=の演算子を実装します。
class MyClass: Comparable {
var value: Int
init(value: Int) {
self.value = value
}
static func < (lhs: MyClass, rhs: MyClass) -> Bool {
return lhs.value < rhs.value
}
static func == (lhs: MyClass, rhs: MyClass) -> Bool {
return lhs.value == rhs.value
}
}
let a = MyClass(value: 3)
let b = MyClass(value: 5)
let c = MyClass(value: 7)
print(a < b) // true
print(b > c) // false
上記の例では、MyClassクラスがComparableプロトコルを準拠していることになり、<、<=、>、>=の演算子を実装しているため、a < b, b > cのように比較ができます。
Codableプロトコル
Codableプロトコルは、オブジェクトをエンコード/デコードするためのプロトコルです。Codableプロトコルは、EncodableとDecodableの2つのサブプロトコルを継承しています。
以下に、Codableプロトコルのコード例を示します。
protocol Codable: Encodable & Decodable { }
上記のプロトコルに準拠するためには、EncodableとDecodableのサブプロトコルに適合する必要があります。
struct MyStruct: Codable {
var name: String
var age: Int
}
let myStruct = MyStruct(name: "John", age: 30)
// Encode
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(myStruct) {
print(String(data: encoded, encoding: .utf8)!)
}
// Decode
let decoder = JSONDecoder()
if let decoded = try? decoder.decode(MyStruct.self, from: encoded) {
print(decoded)
}
上記の例では、MyStructがCodableプロトコルを準拠していることになり、JSONEncoderを使用してMyStructをエンコードし、JSONDecoderを使用してデコードすることができます。
実行結果:
{"name":"John","age":30}
MyStruct(name: "John", age: 30)
まとめ
ここまでSwiftのプロパティについて解説してきました。
皆さんの理解に少しでも役立てたら嬉しいです。
他にもSwiftの記事を書いてるのでよかったら読んでみてください。
また、1人での勉強に限界を感じたら独学の心強い味方「SAMURAI TERAKOYA」も検討してみてください!平均回答スピード30分以内のQ&A掲示板があなたの悩みをスピード解決してくれます!技術的な質問以外にもキャリアの相談にも乗ってくれるのはとてもありがたいですね!