自分がジェネリックスという単語自体が初めて聞く言葉であり、
よく理解できてないのでまとめてみた
参照:The Swift Programming Language (iBooks)
Genericsとは
クラスや関数を使う側が扱う型を指定できるようにする仕組み
これにより、抽象的なコードを記述することを可能にする
関数でのGenerics
関数内で使用する任意の型を定義できる
1. 型制約なし
型の制約がないので、どんな型の場合でも使用することができる
任意の型は名前は自由(ただし、UpperCamelCaseであること)
func 関数名 <任意の型> () {
// 任意の型を使用した処理
}
例) ItemTypeという任意の型を引数と戻り値と内部で使用している
func repeat<ItemType>(item: ItemType, times: Int) -> ItemType[] {
var result = ItemType[]()
for i in 0..times {
result += item
}
return result
}
// ItemTypeがStirngの場合
repeat("knock", 4) // ["knock", "knock", "knock", "knock"]
// ItemTyoeがIntの場合
repeat(10, 4) // [10, 10, 10, 10]
抜粋:: Apple Inc. “The Swift Programming Language”。 iBooks. https://itun.es/jp/jEUH0.l
2. 型制約あり
func 関数名 <任意の型: クラス名 または プロトコル名> () {
// 任意の型を使用した処理
}
例) 使用できる型(T)がEquatableプロトコルに準拠した型だけに制限される
func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
抜粋:: Apple Inc. “The Swift Programming Language”。 iBooks. https://itun.es/jp/jEUH0.l
構造体でのGenerics
クラス、構造体、列挙で使用する任意の型を定義することができる
例) 任意の型の値の要素を持つスタック
struct Stack<T> {
var items = T[]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
var stack = Stack<String>() // <String>と記述することで"String型"で指定する
stack.push("A")
stack.push("B")
stack.items // ["A", "B"]
stack.push(100) // String以外受け付けないのでエラー
抜粋:: Apple Inc. “The Swift Programming Language”。 iBooks. https://itun.es/jp/jEUH0.l
プロトコルでのGenerics
プロトコル内で使用する任意の型を定義できる
このとき、typealias
に任意の型名を指定しておく
例) ItemTypeという任意の型を使用しているプロトコル
プロトコルの宣言
protocol Container {
typealias ItemType
mutating func append(item: ItemType)
var count: Int { get } // { get }はgetter処理の実装を必須とする
subscript(i: Int) -> ItemType { get }
}
抜粋:: Apple Inc. “The Swift Programming Language”。 iBooks. https://itun.es/jp/jEUH0.l
Containerプロトコルに準拠した構造体
ここで任意の型をIntとして固定
// 任意の型をIntとして実装
struct IntStack: Container {
// original IntStack implementation
var items = Int[]()
mutating func push(item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// conformance to the Container protocol
typealias ItemType = Int
mutating func append(item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
抜粋:: Apple Inc. “The Swift Programming Language”。 iBooks. https://itun.es/jp/jEUH0.l
こちらはGenericsの構造体にContainerプロトコルを準拠させたパターン
こちらは、typealiasにセットはしていないが、
任意の型(T型)をitemとappendとsubscriptで使用していることから、
ItemTypeとしてT型を使用していることが推測できる
struct Stack<T>: Container {
// original Stack<T> implementation
var items = T[]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
// conformance to the Container protocol
mutating func append(item: T) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> T {
return items[i]
}
}
抜粋:: Apple Inc. “The Swift Programming Language”。 iBooks. https://itun.es/jp/jEUH0.l
whereを使った型指定
型指定にプロトコルを指定するときに、
さらに、そのプロトコルの内容で細かく制限をかけることができる
例)
Containerプロトコルを実装したC1とC2という型で指定
それ以外に以下の指定を行う
- C1のItemTypeにEquatableプロトコルの型が入っていること
- C1のItemTypeとC2のItemTypeが同じ型であること
func allItemsMatch
<C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, anotherContainer: C2) -> Bool {
// check that both containers contain the same number of items
if someContainer.count != anotherContainer.count {
return false
}
// check each pair of items to see if they are equivalent
for i in 0..someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// all items match, so return true
return true
}
抜粋:: Apple Inc. “The Swift Programming Language”。 iBooks. https://itun.es/jp/jEUH0.l