混合プロトコル
1つの型が複数のプロトコルに準拠している場合、次のような形で型を扱えます。これを混合プロトコルといいます。
protocol<プロトコル名1, プロトコル名2, ...>
複数のプロトコルに準拠した型としてメソッドの引数などを扱いたい場合には便利です。混合プロトコルの例を見てみましょう。
protocol Countable {
var count: UInt { get }
}
class TaskMemo: Printable, Countable {
private var task: String
init(task: String) {
self.task = task
}
var description: String {
return self.task
}
var count: UInt {
return UInt(countElements(self.description))
}
}
func printLongPrintable(printable: protocol<Printable, Countable>) {
if printable.count > 10 {
println(printable.description)
} else {
println("This printable is not long")
}
}
printLongPrintable(TaskMemo(task: "go to shop")) // This printable is not long
printLongPrintable(TaskMemo(task: "work to make money")) // work to make money
この例では、Swiftの標準APIに定義されたPrintableプロトコルと、先ほどの「エクステンションとプロトコルの併用」節で例としたCountableプロトコルに準拠した型を引数に取る関数printLongPrintableを定義しています。
この関数は、countプロパティが10よりも大きいものに対してはescriptionプロパティでログ出力を行い、そうでないものについては規定の文字列を出力するようになっています。
Printableプロトコルは次のように宣言されており、準拠する型はdescriptionプロパティを定義することが求められます。
protocol Printable { var description: String { get } }
プロトコルのオプション機能
Objective-Cと同様に、プロトコルに準拠していても実装することが必須でない場合、プロトコルの宣言の頭に@objcキーワードを、プロトコルのプロパティ、メソッド、subscriptの頭にoptionalキーワードを付けます。これらのキーワードにより、準拠するクラスが、それらのプロパティなどを必ずしも実装しなくてよいことを示せます。
@objc protocol プロトコル名 {
optional var プロパティ名: 型 { get / get set }
optional func メソッド名(引数のリスト) -> 返り値の型
optional subscript(引数のリスト) -> 返り値の型 { get / get set }
}
なお、イニシャライザに関してはoptionalを先頭に記述できません。イニシャライザが記述されたプロトコルに準拠する型は、必ずそれを実装する必要があります。
@objcが付いたプロトコルの実装例を見てみましょう。
@objc protocol ListViewDataSource {
optional var numberOfList: UInt { get }
optional func titleForElementAtIndex(index: UInt) -> String
}
class ReservationDataSource: ListViewDataSource {
private var reservations: [String: String]
init(reservations: [String: String]) {
self.reservations = reservations
}
var numberOfList: UInt {
return UInt(reservations.count)
}
}
この例で宣言しているListViewDataSourceは、各データのタイトルを表示するビューを提供するためのプロトコルです。ListViewDataSourceは自身に準拠するクラスに対して、numberOfListプロパティやtitleForElementAtIndexメソッドを任意で実装するように要求しています。
ReservationDataSourceクラスは、ListViewDataSourceプロトコルに準拠することでビューの要素数や各要素のタイトルを決められます。また、ReservationDataSourceクラスは予約リストのデータソースをDictionaryを用いて初期化します。
オプション機能の定義されたプロトコルに準拠した型が実際にオプション機能を実装しているかどうかは、プロトコルのプロパティやメソッドの名前の直後に「?」を付けることでチェックできます。本連載第三回で取り上げたオプショナルチェーンと同様に、チェックの結果はオプショナル列挙型にくるまれた上で渡されます。
先ほどの例を用いて、プロトコルの型注釈を併用しつつ、そのことを確認してみましょう。
var dataSource: ListViewDataSource =
ReservationDataSource(reservations: ["john": "go to shopping"])
dataSource.numberOfList? // Some(1)
dataSource.titleForElementAtIndex?(1) // nil
dataSource変数はデータ格納時、表面的にはListViewDataSource型として扱われます。実際にそれらが実装されているかどうかは、プロパティやメソッドの後ろに「?」付けてチェックしています。
dataSource変数の中身はReservationDataSourceオブジェクトで、かつnumberOfListメソッドは実装されているため、「?」を付けたアクセスによって、実装内容の返り値をオプショナル列挙型でくるんだ形(Some(1))で取得できます。
一方、titleForElementAtIndexメソッドはReservationDataSourceクラスに実装されていないため、呼び出し結果としてnil (=Optional.None) が返ってきます。
次回は
今回はエクステンションとプロトコルについて扱いました。
定義済みの型に対して計算プロパティやメソッドを適切な場面に追加すれば、繰り返しのコードも避けられるようになり、よりメンテナンス性の高いコードが実現できます。
また、プロトコルを用いれば共通の機能を持った型をまとめて扱うことができます。さらに、次回に解説するジェネリクスと組み合わせることで、より汎用的なモジュールを作成できます。
次回はジェネリクスと演算子を中心とした説明をしていきます。お楽しみに。
