<T>って書いてるアレ
アプリのソースコードやライブラリなどを眺めていると、<T>っていう謎の記述をよく見かける。これはSwiftが汎用的な記述を可能にするための、ジェネリクスという記法らしい。
特徴
クラスや関数などで良い感じに使うと、抽象的にコード記述することができる!!
ちなみにジェネリクスで定義された型はジェネリック型と呼ぶ。(薬?)
書式
func 関数名 <任意の型> () {
// 何らかの処理
}
使われている場所
実は、ジェネリクスは知らぬ間に結構使っているもので、Swiftにある標準ライブラリなどは頻繁に使われています。例えば、値を入れ替えるだけのswap関数などは以下のように定義されています。
swap<T>(inout a: T, inout _ b: T)
このジェネリクスの何がいいのか?
例えば、同じような動作で以下のようなメソッドを書いたとします。
func mySwap(inout a: Int, inout b: Int) {
// 処理
}
もし、これをIntやFloatもDoubleもなどと対応していると、その数分だけ関数を定義しなければいけません!めんどい!こういう問題を解決してくれるものがジェネリクスということらしいです。
実際の処理
ちなみにジェネリクスは、通常の関数などで定義した型とは違って、プログラムで記述された時に与えられた具体的な値の型をTにあたはめて推論し、矛盾がなければコンパイルされるようです。
簡単な例
全く良い例が浮かばなかったので、今回は会計する時に、支払う金額が正しく購入金額より多く支払われるかを判定したいという場合のプログラムを書いてみました。
func cash(pay: Int, purchase: Int) -> Bool {
return pay == purchase ? true: false
}
cash(1000, purchase: 1000) // true
ここで、例えば、ありえないですが、新たにお金が1銭とか2銭出せるようになったとしましょう。この場合、いまの通貨では小数点部分として扱われます。という想定で上のメソッドで、小数点も比較したい!となったとします。
func cash(pay: Int, purchase: Int) -> Bool {
return pay == purchase ? true: false
}
cash(1000.1, purchase: 1000) // エラーで死にます
上記の例では当然ながら、エラーで落ちます。これは、冒頭にも説明した通りで、Intしか許容していないためです。ではどうするか?
ここでジェネリクス使えば行ける!!と感じますが、見事に死にます。
func cash<T>(pay: T, purchase: T) -> Bool {
return pay == purchase ? true : false
}
cash(1000.1, purchase: 1000) // エラーで死にます。
実は、なんでも許容するとは書いてますが、制限はしっかりあります。その制限の中で許容されている動きかどうかが判断されて初めて、実行されるのがジェネリクスの便利なところでもあり、少し理解がしにくいところでもあります。
func calc<T: Equatable>(atai1: T, atai2: T) -> Bool {
return atai1 == atai2 ? true : false
}
calc(1000.0, atai2: 1000) // true
<T: Equatable>という記述が追加されただけで、急に処理が実行されました。これはプロトコルというもので、Equatableというのは、"=="について記述がされている制約です。Int, Float, DoubleはEquatableに適合した型なので、問題ないと判断され実行されます。
急に出てきたプロトコルについて
Swiftの標準ライブラリはジェネリクスとプロトコルの組み合わせが肝になってるようなので、勉強した方が良い記法だと思います。
まとめ
上手に使えば、汎用的なものがサッと書けるので、是非マスターしたい。
プロトコルについては、また書いてみます。