今回はSwiftの機能の1つでもあるジェネリクスについて
初学者にも分かりやすいように解説していきたいと思います。
この記事を読むと、
・ジェネリクスとは
・ジェネリクスの実装方法
・ジェネリクスとAny型の違い
などを学ぶ事ができます。
ぜひ最後まで読んでいってください。
ジェネリクスとは
Swiftのジェネリクスとは、関数やクラスなどのデータ型を指定しないままに、複数のデータ型を扱えるようにする機能です。つまり、関数やクラスに対して、「この関数はあらゆるデータ型を受け入れる」「このクラスはあらゆるデータ型のインスタンスを生成できる」といった汎用的な定義をすることができます。これにより、同じロジックを複数のデータ型に対して使用することができるようになり、コードを簡潔かつ再利用性の高いものにすることができます。
ジェネリクスの実装方法
ジェネリクスを使用するためには、関数やクラスの名前の後に小文字のTなどの任意の文字を使用して<T>のように記述します。そして、これらのジェネリックスの文字を使用して、関数やクラス内部でデータ型を参照します。
func swapValues<T>(a: inout T, b: inout T) {
let temp = a
a = b
b = temp
}
var num1 = 5
var num2 = 10
swapValues(a: &num1, b: &num2)
print(num1, num2) // 10, 5
上記の例では、swapValues関数はジェネリクスを使用しています。それにより、int型だけでなく、文字列型などの他の型の値も交換することができます。
ジェネリクスによって、同じロジックを複数のデータ型に対して使用することができるようになり、コードを簡潔かつ再利用性の高いものにすることができます。
また、ジェネリクスはクラスや構造体、enumに対しても使用することができます。
class MyClass<T>{
var value:T
init(value:T){
self.value = value
}
}
let myClass = MyClass<String>(value: "Hello")
このようにジェネリクスを使用することで、MyClassクラスが文字列型の値を持つインスタンスを生成することができます
ジェネリクスはSwiftにおいて非常に重要な機能の一つで、型安全かつ再利用性の高いコードを書くために使用することができます。
ジェネリクスを使う場合と使わない場合
ジェネリクスを使う場合:
func swapValues<T>(a: inout T, b: inout T) {
let temp = a
a = b
b = temp
}
var num1 = 5
var num2 = 10
swapValues(a: &num1, b: &num2)
print(num1, num2) // 10, 5
var str1 = "hello"
var str2 = "world"
swapValues(a: &str1, b: &str2)
print(str1, str2) // world, hello
上記の例では、swapValues関数はジェネリクスを使用している。それにより、int型だけでなく、文字列型などの他の型の値も交換することができる。
ジェネリクスを使わない場合:
func swapValues(a: inout Int, b: inout Int) {
let temp = a
a = b
b = temp
}
var num1 = 5
var num2 = 10
swapValues(a: &num1, b: &num2)
print(num1, num2) // 10, 5
上記の例では、swapValues関数はジェネリクスを使用していない。それにより、int型だけしか交換することができない。
ジェネリクスとAny型の違いとは?
SwiftのジェネリクスとAny型は、型を指定しない場合に使用することができますが、使用方法や機能の違いがあります。
ジェネリクスは、関数やクラスなどで使用し、複数のデータ型を扱うことができるようにする機能です。ジェネリクスを使用すると、関数やクラスがどのようなデータ型を受け入れるか明示的に定義することができ、それによって型安全なコードを書くことができます。
一方、Any型は、どのような型でも受け入れることができる特殊な型です。Any型を使用すると、関数やクラスがどのようなデータ型も受け入れるようになりますが、それに伴って型安全性が低くなります。
ジェネリクスは、複数のデータ型を扱うことができるが型安全である一方で、Any型はどのようなデータ型も扱うことができるが型安全性が低い。
つまり、ジェネリクスを使用することで、型安全なコードを書くことができますが、Any型を使用することでは、型安全性を犠牲にして、汎用的なコードを書くことができます。
型制約について
Swiftの型制約は、ジェネリクス関数やクラスなどで使用する型に対して特定の条件を設定することです。それによって、関数やクラスがどのようなデータ型を受け入れるかを制限し、型安全性を高めることができます。
型制約を設定するには、ジェネリクス関数やクラスの型パラメーターに対して、特定の型を継承していることや、特定のプロトコルを実装していることなどを条件として設定します。
func swapValues<T: Comparable>(a: inout T, b: inout T) {
let temp = a
a = b
b = temp
}
上記の例では、swapValues関数はジェネリクス関数で、T型がComparableプロトコルを実装していることが条件となっています。それにより、int型やDouble型などがComparableプロトコルを実装している型だけが使用することができます。
型制約は、ジェネリクス関数やクラスが特定の型だけを受け入れるようにすることで、型安全性を高めることができます。また、型制約を使用することで、特定の機能を持つ型だけを使用することができるようになり、コードの信頼性や可読性を高めることができます。
まとめ
ここではジェネリクスについて、
・ジェネリクスとは
・ジェネリクスの実装方法
・ジェネリクスを使う場合と使わない場合
・ジェネリクスとAny型の違いとは?
・型制約について
などについて解説しました。
ジェネリクスを活用することで、汎用性と型安全性を両立させることができます。
また、どれくらい汎用的にするかは型制約によって調節できます。
強い型制約を設ければ汎用性は限定的になりますが、その分だけだけ型の性質を利用した具体的な処理の実装が可能となります。
皆さんの理解に少しでも役立てたら嬉しいです。
他にもSwiftの記事を書いてるのでよかったら読んでみてください。
また、1人での勉強に限界を感じたらiOSに特化したプログラミングスクール「iOSアカデミア」も検討してみてください!無料相談可能で「最短・最速」でiOSエンジニアになれるように手助けしてくれます。