Swift 型消去 (Type Erasure)
try! Swiftで発表のあった型消去の話。
この例ではSwiftにおける型消去技法を使用して、結城さんの 増補改訂版Java言語で学ぶデザインパターン入門 Iteratorパターンを愚直に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