ジェネリックプロトコルについて、わかりやすさを意識して、まとめてみました。
associatedtype
やSelf
ってなんか取っ付きにくいなーと感じている人向けです。
What is Generic Protocol?
ジェネリックプロトコル(Generic Protocol)とは、ジェネリクスを使ったプロトコルです。1
ClassやStructでは<T>
などの型パラメーター(Type Parameter)を使う一方、Protocolではassociatedtype
またはSelf
を宣言します。
associatedtype
やSelf
も型パラメーター同様、宣言された段階ではまだ抽象的な存在で、Protocolを採用するクラス宣言で初めて具体的な実装がなされます。The Swift Programming Languageでは、型の__プレースホルダー__と表現されています。
ジェネリックプロトコルの例
話をわかりやすくするために、Animal Protocolというジェネリックプロトコルを作ってみましょう。
protocol Animal {
associatedtype FoodType // ※1
func eat(food: FoodType) // ※2
func makeFriend(friend: Self) // ※3
}
ここでAnimal Protocolが言っているのは、以下のとおりです。
- Animal Protocolを使いたければ、クラス宣言のときに、何かしらの型(Type)とFoodTypeを結びつけてね(※1)
- そうしたら、func eatのfoodは自動的にその型になるよ(※2)
- func makeFriendで友達になれるのは同じクラスだよ。またSwiftの言語仕様的にサブクラスもOKだよ(※3)
適合するクラスを実装
Animal Protocolに言われたとおり、Catクラスを実装してみます。2
class Cat: Animal {
typealias FoodType = Mouse
func eat(food: FoodType) {
print("yum")
}
func makeFriend(friend: Cat) {
print("Got a new friend!")
}
}
さて、associatedtype
だった箇所にtypealias
が出てきましたね。typealias
とは、その名の通り、__型の別名(エイリアス)__のことです。CatクラスではMouseをFoodType
として別名指定しました。これでCatのFoodTypeといえばMouseとなり、ネコはfunc eatでネズミを食べられるようになりました
まとめ
- プロトコルをジェネリックに作るには
associatedtype
もしくはSelf
のどちらかを使う -
associatedtype
で指定された抽象パラメーターを、クラス実装時に具体的な型で指定する。このとき、typealias
を使う。別解↓のようにtypealias
を使わない場合、抽象パラメーターがすでに割り当てられている箇所に具体的な型を入れる。 -
Self
には、適合するクラスあるいはサブクラスが自動的に割り当てられる。
別解
Animalプロトコルに適合したCatの例は以下でも可能です。
1. FoodTypeプレースホルダーに具体的な型を指定する
class Cat: Animal {
// 引数をMouseにする
func eat(food: Mouse) {
print("yum")
}
func makeFriend(friend: Cat) {
print("Got a new friend!")
}
}
2. ジェネリクスの型パラメーターで型を指定する
// 型パラメーターで制約をつける
class AnyAnimal<FoodType> {
func eat(food: FoodType) {
print("yum")
}
func makeFriend(friend: AnyAnimal) {
print("Got a new friend!")
}
}
//...
let x = AnyAnimal<Mouse>()
-
The Swift Programming Languageに、"Generic Protocol"という呼び名は出てきませんが、英語圏ではこの呼び名が一般的です。 ↩
-
このときCatクラスはAnimal Protocolに__適合する__、__準拠する__と表現します。もしくはAnimal Protocolが__採用される__とも言います。前者は英語では"Cat Class conforms to Animal Protocol"、後者は"Animal Protocol is adpoted"です。 ↩