今回はジェネリクスについてまとめます
ジェネリクス(Generics)
は指定したタイプで柔軟に動作する再利用可能な関数や型を指定できる機能です
ジェネリクスを使用するには、関数名のあとに<T>
を指定します
<T>
は型引数(型パラメータ)と呼ばれるもので、この関数ではTという型を使用することを意味しています
<T>
を指定すると、関数が呼び出されたときにSwiftのシステムで自動で適切な型に置き換えて処理を実行してくれます
ジェネリクスの型は<T>
でなくても構いせんが、慣例として<T>
が使われています
ジェネリクスは関数やクラスで使用することができます
Generics関数
func isEqualCheck<T: Equatable>(x: T, y: T) -> Bool { return x == y }
上記ではxとyが等しいかをチェックしています。
Equatable
をつけているのはTがEquatableプロトコルに準拠している型というのを表しています。
StringやFloat、BoolなどはEquatableプロトコルに準拠しているので使用できます。
逆に型によっては使用できない可能性がある場合(今回の場合に比較や+-などのオペレータの場合)にはTがどのプロトコルに準拠しているかを明示する必要があります
Genericsクラス
ジェネリクスクラスは次のようにして使います
class GenericsClass<T> { var item: T init(item: T) { self.item = item } } let generics1 = GenericsClass(item: 100) let generics2 = GenericsClass(item: "TEST")
指定方法はGenerics関数と同じで型引数(<T>
)を指定するだけです
Genericsプロトコル
Swiftではプロトコルにもジェネリクスを使用することができます
例えば、次のように使います
protocol TestProtocol { associatedtype T var item:T { get } func testValue(item:T) } // 1つ目の継承クラス class Test1:TestProtocol { var item:Int = 100 func testvalue(item: Int) { self.item += 100 } } // 2つ目の継承クラス class Test2:TestProtocol { var item:String = "100" func fruitvalue(item: String) { self.item = item + "円です" } }
プロトコルでジェネリクスを使用するためには、定義内でassociatedtype T
のように任意の型を指定する必要があります
型制約
Generics関数の説明で、TがEquatableプロトコルに準拠している型というのを表しました。 このように準拠するプロトコルやスーパークラスなどの型引数に制限を追加することがあります。 これを型制約といいます
型制約を追加することでジェネリクス関数やジェネリクス型を細かくコントロールすることができます
今回はwhere
を使った制限を紹介します!
func sort<T: Collection>(numbers: T) -> [T.Iterator.Element] where T.Iterator.Element: Comparable { return numbers.sorted() }
上記の例の場合には型引数TはCollectionプロトコルに準拠している上にwhere T.Iterator.Element: Comparable
で定義されているようにComparableプロトコルに準拠している
T.Iterator.Elementに制限されます
ちなみに、Elementは複数要素のそれぞれの型を表しています
このように細かい制限を加えることによって想定外の型が入る余地を省いています。ジェネリクスはどんな方も入る可能性があるため注意が必要です