はじめに
コードでよく見る<T>の意味を理解するために、ジェネリクス(ジェネリック関数とジェネリック型)を使ってみました。
ジェネリックの必要性
以下のサンプルプログラムの関数はInt型の値を受け取り値が同じか判定するものです。
//受け取った引数の値が同じかどうかを判定する
func equalJudge(x:Int,y:Int) -> Bool {
return x == y
}
let x :Int = 10
let y :Int = 11
equalJudge(x: x, y: y) //false
これはInt型のみを受け取ることができる関数で、その他のDouble型などは受け取れません。
Double型を受け取る場合は別途、別の関数を用意する必要があります。
そんなときに便利なのがジェネリクスです。
ジェネリクスを使用することで、Int型でもDouble型でも両方の値を使用できる
汎用的な関数を作成することができます。
ジェネリック関数
//受け取った引数の値が同じかどうかを判定する
func equalJudge<T:Equatable>(x:T,y:T) -> Bool {
return x == y
}
equalJudge(x: 10, y: 11) //false
equalJudge(x: 10.5, y: 10.5)//true
equalJudge(x: "10.5", y: "10.5")//true
先程のサンプルプログラムと比較してみましょう。
Intとなっていた箇所がTに変わっています。
また、関数名の後ろに<T:Equatable>が追加されています。
このTは汎用的な型を表しており、関数を呼び出し時にIntやDoubleなどの型として使用されます。
例えば、引数のx,yの値がInt型の場合、T型はInt型として決定されます。
よく使われるのはTですが、T以外でも問題ありません。
また、<T:Equatable>はTがEquatableプロトコルに準拠していることを示しており、
xとyを比較することが可能になっています。
ジェネリック型
struct ValueStruct<T>{
var valueA:T
var valueB:T
}
let val = ValueStruct<String>(valueA:"Value" , valueB: "Value") //ValueStruct<String>
let val2 = ValueStruct(valueA: "Value", valueB: "Nothing")//ValueStruct<String>
ジェネリック型についても、ジェネリック関数と同様にTは汎用的な型として扱われます。
上記の例ではTをValueStructのプロパティの型として使用しています。
初期化時に<String>と型を明示することでTはString型と決定されます。
また、型を明示していない場合でも、型推論により自動的にTをString型と決定することが可能です。