タコさんブログ

プログラミングメモと小言

Swift 型消去 (Type Erasure)

try! Swiftで発表のあった型消去の話。
この例ではSwiftにおける型消去技法を使用して、結城さんの 増補改訂版Java言語で学ぶデザインパターン入門 Iteratorパターンを愚直にSwiftで実装する。

前提知識

環境

Iteratorインターフェース

要素の数え上げをするインターフェース。

protocol Iterator {
  typealias Element
  func next() -> Element?
}

Aggregateインターフェース

SwiftではJava版のように、以下のようにAggregateを定義することはできない。

protocol Aggregate {
  func iterator() -> Iterator
}

この場合、下のようなエラーになる。

error: protocol 'Iterator' can only be used as a generic constraint because it has Self or associated type requirements

SwiftではAssociated Typeを使って定義されたプロトコル(抽象型)をそのまま使うことはできないので、AnyIteratorを返すようにする。

protocol Aggregate {
  typealias Element
  func iterator() -> AnyIterator<Element>
}

AnyIteratorクラス(型消去法)

IteratorクラスのAssociated Typeを消去するためのラッパークラス。
Associated Typeからジェネリクス型に変換する。

class AnyIterator<Element>: Iterator {
  private let _next: () -> Element?

  init<Base: Iterator where Base.Element == Element>(_ base: Base) {
    _next = base.next
  }

  func next() -> Element? {
    return _next()
  }
}

Bookクラス

本を表すクラス。

class Book {
  let name: String

  init(name: String) {
    self.name = name
  }

  func getName() -> String {
    return name
  }
}

BookShelfクラス

本棚を表すクラス。

class BookShelf: Aggregate {
  var books: [Book] = []

  func getBookAt(index: Int) -> Book? {
    return index < books.count ? books[index] : nil
  }

  func appendBook(book: Book) {
    books.append(book)
  }
  // Aggregateの実装
  func iterator() -> AnyIterator<Book> {
    let bookShelfIterator = BookShelfIterator(bookShelf: self)
    return AnyIterator(bookShelfIterator)
  }
}

BookShelfIterator

BookShelfをイテレートするクラス。

class BookShelfIterator: Iterator {
  let bookShelf: BookShelf
  var index: Int = 0

  init(bookShelf: BookShelf) {
    self.bookShelf = bookShelf
  }
  // Iterator next
  func next() -> Book? {
    let book = bookShelf.getBookAt(index)
    index++
    return book
  }
}

実行例

let bookShelf = BookShelf()
// 本棚(BookShelf)に本(Book)を入れる
bookShelf.appendBook(Book(name: "Book A"))
bookShelf.appendBook(Book(name: "Book B"))
bookShelf.appendBook(Book(name: "Book C"))
bookShelf.appendBook(Book(name: "Book D"))
// bookをイテレートするAnyIterator<Book>を生成
let bookIterator = bookShelf.iterator()
// イテレート
while let book = bookIterator.next() {
  print(book.getName())
}

この出力は、

Book A
Book B
Book C
Book D

参考URL

Note