Swift

Swiftのジェネリックスについてのメモ

More than 3 years have passed since last update.

自分がジェネリックスという単語自体が初めて聞く言葉であり、
よく理解できてないのでまとめてみた

参照: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