プロトコルを定義するとき、プロトコルの定義の一部に関連型を宣言すると有益な場合があります。関連型 (associated types) は、プロトコルの一部として使用される型に、プレースホルダ名を与えます。その関連型に使用する実際の型は、プロトコルが採用されるまで指定されません。関連型は associatedtype
キーワードで指定します。
関連型の動作
次は、関連型 ItemType
を宣言するプロトコル Container
の例です。
protocol Container {
associatedtype ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
Container
プロトコルは、あらゆるコンテナが提供する必要がある 3 つの必須性能を定義しています。
append(_:)
メソッドでコンテナに新しいアイテムを追加できることInt
値を返すcount
プロパティでコンテナにあるアイテム数にアクセスできることInt
インデックス値を取るサブスクリプトでコンテナの各アイテムを取り出せること
このプロトコルには、コンテナのアイテムがどのように保管され、どのような型が許容されるかを指定していません。プロトコルは、型が Container
とみなされるために提供する必要がある 3 つの機能を指定しているだけです。3 つの要件を満たす限り、準拠する型に追加の機能を提供させることができます。
Container
プロトコルに準拠する型は、保管する値の型を指定できる必要があります。特に、コンテナに適した型のアイテムのみ追加されるようにし、サブスクリプトで返されるアイテムの型について明確にする必要があります。
これらの要件を定義するためには、Container
プロトコルに、特定のコンテナの型を知ることなく、コンテナが保持する要素の型を参照するための手段が必要です。append(_:)
メソッドに渡される値がコンテナの要素の型と同じ型であり、コンテナのサブスクリプトで返される値がコンテナの要素の型と同じ型であることを、Container
プロトコルに指定する必要があります。
これを達成するために、associatedtype ItemType
として記述する関連型 ItemType
を Container
プロトコルに宣言します。プロトコルには ItemType
が何であるかを定義せず、準拠する型がその情報を提供するようにします。それにもかかわらず、Container
に期待される振る舞いが強制されるように、Container
にあるアイテムの型を参照し、append(_:)
メソッドとサブスクリプトで使用する型を定義する手段を ItemType
エイリアスが提供します。
次はジェネリックでないバージョンの IntStack
型で、Container
プロトコルに準拠するようにしています。
struct IntStack: Container {
// IntStack のオリジナル実装
var items = [Int]()
mutating func push(item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// Container プロトコルに準拠
typealias ItemType = Int
mutating func append(item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
IntStack
型は Container
プロトコルの 3 つの要件をすべて実装し、要件を満たすために各ケース内で IntStack
型の既存の機能の一部をラップしています。
さらに、Container
のこの実装で使用する適切な ItemType
は、Int
型であると指定しています。Container
プロトコルのこの実装では、typealias ItemType = Int
の定義で抽象的な ItemType
型を、具体的な Int
型に変えています。
Swift
の型推論のおかげで、実際には IntStack
の定義の一部として ItemType
の具体的な Int
を宣言する必要がありません。IntStack
が Container
プロトコルの要件すべてに準拠しているため、単に append(_:)
メソッドの item
パラメータの型とサブスクリプトの戻り値の型から、Swift は使用する適切な ItemType
を推論することができます。実際、上のコードから typealias ItemType = Int
の行を削除した場合でも、ItemType
に使用される型が明確であるため、依然としてすべて動作します。
また、Container
プロトコルに準拠するジェネリックな Stack
型にすることもできます。
struct Stack<Element>: Container {
// Stack<Element> のオリジナル実装
var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// Container プロトコルに準拠
mutating func append(item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
今回は、型パラメータ Element
が append(_:)
メソッドの item
パラメータの型、およびサブスクリプトの戻り値の型として使用されています。そのため、この特定のコンテナの ItemType
として使用する適切な型を、Swift が推論することができます。
関連型を指定するために既存の型を拡張
Adding Protocol Conformance with an Extension で説明されているように、プロトコル準拠を追加するために既存の型を拡張することができます。これには、関連型のプロトコルが含まれます。
Swift の Array
型は、すでに append(_:)
メソッドや count
プロパティ、Int
インデックスで要素を取り出すサブスクリプトを提供しています。これら 3 つの性能は、Container
プロトコルの要件にマッチします。つまり、Array
がプロトコルを採用すると宣言するだけで、Container
プロトコルに準拠するよう Array
を拡張することができます。Declaring Protocol Adoption with an Extension で説明されているように、空のエクステンションを記述します。
extension Array: Container {}
配列の既存の append(_:)
メソッドとサブスクリプトが、上で見たジェネリックな Stack
型と同じように、ItemType
に使用する適切な型を Swift が推論できるようにします。このエクステンションの定義後、あらゆる Array
を Container
として使用することができます。
Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.