前回はジェネリクスの概要についてめちゃめちゃざっくりと簡単な説明をしました。
今回はもう少しだけ詳しくジェネリクスの説明をしていこうと思います。
お酒を飲みながら書いているので、間違っているところがあるかもしれないですが、その時はコメントから教えてください。
前回の記事はこちら https://shuhey-hashimoto.com/swift/swiftジェネリクスとはどう使うの/
ジェネリクス型
制約をつけたい
いくら抽象化して汎用的にしたいとはいっても、どんな方でも受け入れていると不整合が起きてしまいます。ですので型に制約をつけたい。そんなときは以下のように型引数Tに制約をつけましょう。
struct 構造体名<T: スーパークラスまたはプロトコル> {
...
}
class クラス名<T: スーパークラスまたはプロトコル> {
...
}
enum 列挙型名<T: スーパークラスまたはプロトコル> {
...
}
これだけではちょっとわかりにくいと思うので、例を一つあげましょう。
今回は構造体の中で計算処理をさせたいので、数値だけを受け入れる制約にします。
struct sample<T: Numeric> {
private let a: T
private let b: T
func sum() -> T {
return a + b
}
}
こんな感じにプロトコルNumericを指定することで足し算処理を実装することができます。
拡張させたい
拡張も簡単です。一般的な構造体などと一緒で、extensionを使用することで拡張することができます。
struct sample<T: Numeric> {
private let a: T
private let b: T
}
extension sample {
func sum() -> T {
return a + b
}
}
上記のようにもちろんextensin側でも型引数を使用できます。
特定の型の時だけ拡張させたい
例えばDoubleの時だけ数値を丸めたいとか、引数の型に応じて制約をかけたい場合はwhereを使用します。
struct sample<T: Numeric> {
private let a: T
private let b: T
}
extension sample where T == Int {
func sum() -> T {
return a + b
}
}
extension sample where T == Double {
func sum() -> T {
return round(a + b)
}
}
ジェネリクス関数
制約をつけたい
関数の場合もジェネリクス型と変わりません。以下のように型引数にスーパークラスまたはプロトコルの制約を持たせることができます。
func 関数名<T: スーパークラスまたはプロトコル>(_ 引数: T) {
...
}
わかりやすいように例を挙げるとこんな感じです。
func sample<T: Numeric>(_ a: T, _ b: T) -> T{
return a + b
}
より詳細な制約をつける
ジェネリクス型の時のようにwhereを使用すると、より詳細な制約をかけることができます。
func 関数名<T: プロトコル>(_ 引数: T) -> 戻値型 where プロトコルの関連型: プロトコル or スーパークラス
例えばコレクション型のTに対して、コレクションできる要素の型をNumericに準拠した型に限定する場合、以下のように書きます。
func sample<T: Collection>(_ a: T) -> T.Element where T.Element: Numeric{
return a.reduce(0, +)
}
コメント