詳解Swift#8(Chapter11 拡張)
前回に続き、詳解Swiftの写経を続ける。
- 作者: 荻原剛志
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2015/12/25
- メディア: 単行本
- この商品を含むブログを見る
chapter11 拡張
継承や型そのもののコード変更を行わずに、既に存在している型にメソッドやプロパティを追加できる機能。Objective-Cでは「カテゴリ」と呼ばれていたらしい。強力だと言っている。
拡張の宣言
extension 型名: プロトコル { イニシャライザ 計算型プロパティ メソッド その他の定義 }
という定義の仕方をする。
- 既存の型に対する拡張
extension String { var length: Int { return self.characters.count } }
クラスに対して拡張定義をした場合、サブクラスにも適用される。ただ、その拡張はfinalで定義されたものとみなされるのでサブクラス側で変更したりはできない。
拡張定義とプロトコルへの適合
truct Ounce { var mL:Double = 0.0 static let ounceUS = 29.5735 init(ounce:Double) { self.ounce = ounce } var ounce: Double { get { return mL / Ounce.ounceUS } set { mL = newValue * Ounce.ounceUS } } }
このOunce構造体にプロトコル拡張を定義すると以下。
:walking: extension Ounce : FloatLiteralConvertible { init(floatLiteral value: Double) { self.init(ounce: value) } }
プロトコル拡張
- 年月日を整数値で表現するプロトコル
protocol Datable { var year: Int { get } var month: Int { get } var day: Int { get } }
このプロトコルを拡張してみる。
protocol Datable { var year: Int { get } var month: Int { get } var day: Int { get } } // ツェラーの公式 func Zeller(var m:Int, _ d:Int, var _ y:Int) -> Int { if m < 3 { m += 12; --y } let leap = y + y / 4 - y / 100 + y / 400 return (leap + (13 * m + 8) / 5 + d) % 7 } extension Datable { var dayOfWeek: Int { return Zeller(month, day, year) } } struct Date : Datable { var year, month, day: Int } let d1 = Date(year:1992, month:7, day:19) print(d1) print(d1.dayOfWeek)
プロトコルを拡張することで、メソッド、添字付けなど実装を拡張することができる。プロトコル拡張で定義したメソッドなどの実装は 既定実装 と呼ぶ。
- 2つのプロトコル拡張
protocol DateType { var year: Int { get } var month: Int { get } var day: Int { get } var area: String? { get } } extension DateType { func toString() -> String { return "\(year). \(month). \(day)" } } protocol TimeType { var hour: Int { get } var minute: Int { get } var area: String? { get } } extension TimeType { func toString() -> String { var s = (hour < 10 ? " " : "") + "\(hour)" s += ":" + (minute < 10 ? "0" : "") + "\(minute)" if let a = area { s += " (\(a))" } return s } } struct Date : DateType, TimeType { let year, month, day: Int let hour, minute: Int let area: String? init(_ y:Int, _ m:Int, _ d:Int, _ t:(Int, Int), area:String? = nil) { year = y; month = m; day = d; (hour, minute) = t; self.area = area } }
プロトコル拡張の既定実装は宣言(静的)であり、クラスの継承のように動的に動作するものではない。