読者です 読者をやめる 読者になる 読者になる

Pebble's Diary

プログラマーの作業メモ

swift ジェネリクス、プロトコル

swift
struct Stack<Element> {
    var items = [Element]()
    mutating func push(item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

このジェネリック構造体を拡張してみる。
ジェネリック型を拡張する際は、extensionの定義の一部としてパラメータリストを与える必要がない。
その代わり、originalの型定義からの型パラメータリストはextensionのbody内で利用でき、オリジナルの型パラメータ名はオリジナルの定義からの型パラメータへの参照に利用できる。 以下で、読み込み専用のcomputed プロパティtopItemを定義している。

extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

Adding Protocol Conformance with an Extension

class Dice {
    let sides: Int
    let generator: RandomNumberGenerator
    init(sides: Int, generator: RandomNumberGenerator) {
        self.sides = sides
        self.generator = generator
    }
    func roll() -> Int {
        return Int(generator.random() * Double(sides)) + 1
    }
}

protocol TextRepresentable {
    var textualDescription: String { get }
}

DiceクラスをTextRepresentableプロトコルに適合したクラスにextensionしてみる。

extension Dice: TextRepresentable {
    var textualDescription: String {
        return "A \(sides)-sided dice"
    }
}

Protocol Inheritance

以下のようにプロトコルは継承できる。

protocol PrettyTextRepresentable: TextRepresentable {
    var prettyTextualDescription: String { get }
}

Protocol Extensions

プロトコルに対して、メソッド実装やプロパティ実装をextensionできる。

protocol RandomNumberGenerator {
    func random() -> Double
}

extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

プロトコルは引数と戻り値が決まっているので、extensionで追加したメソッドやプロパティはその関数の呼び出しを利用した形の実装となっているところがミソである。

Associated Types

ItemTypeをAssociated Typeとして定義したContainerプロトコルである。

protocol Container {
    typealias ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

このプロトコルに準拠した構造体は以下のように作る。
Associated TypeであるItemTypeの型を固定しているところがミソである。

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  // <- ここでItemTypeの型を固定している
    mutating func append(item: Int) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}