【Swift入門】ジェネリクス(generics)の使い方を分かりやすく解説!

SwiftではIntやStringなどの型に左右されずに、柔軟に動作する関数やクラスを定義することができるジェネリクス(generics)という機能が用意されています。

この記事では、

・ジェネリクス(generics)とは
・ジェネリクスの考え方や使い方

というジェネリクス(generics)の基本的な解説から、

・ジェネリクスのクラスを使用する方法
・ジェネリックプロトコルの使い方

などの応用的な使い方に関しても解説していきます。

今回はそんなジェネリクス(generics)の使い方についてわかりやすく解説します!

※この記事ではSwift4.0を使用しています。

目次

ジェネリクス(generics)とは

ジェネリクス(generics)とは、ユーザーが定義した要件に応じて、指定したタイプで柔軟に動作する再利用可能な関数や型を指定できる機能です。

ジェネリクスはSwiftの特徴的な機能の1つで多くの機能でジェネリクスは使用されています。

たとえば、Swiftの配列型やディクショナリ型(辞書)でも型に左右されずに値を格納したり取り出すことが可能です。

ジェネリクス関数の定義:

func 関数名<T>(引数名:型, …)-> 戻り値の型{
  …処理…
}

ジェネリクスを使用するには、関数名のあとに<T>を指定します。

<T>は型パラメータと呼ばれるもので、この関数ではTという型を使用することを意味しています。

<T>を指定すると、関数が呼び出されたときにSwiftのシステムで自動で適切な型に置き換えて処理を実行してくれます。

ジェネリクスの型は<T>でなくても構いせんが、慣例として<T>が使われています。

ジェネリクスの考え方・使い方

ここではジェネリクスの考え方や具体的な使い方について見ていきましょう。

通常の関数の使い方

以下はInt型の引数を2つ受け取ってInt型の値を返却する関数です。

func samplefunc(num1: Int, num2: Int) -> Int {
    return num1 + num2
}

print(samplefunc(num1:100, num2:200)) // 300

関数で受け取る型が分かりきっている場合は、このような指定方法でも問題ありません。

しかし、もしFloat型やDouble型などの異なる引数を渡したい場合、この関数では以下のように小数点を含んだ引数を指定すると、コンパイルエラーになります。

samplefunc(num1:100.5, num2:200.3)

次項ではそんな問題を解決するジェネリクスの基本的な使い方について解説します。

ジェネリクスの使い方

ジェネリクスを指定すると、1度の定義であらゆる型に対応することができます。

前項で型の異なる引数を関数に渡したいとき、通常なら関数のオーバーロードを作成する必要がありますが、ジェネリクスなら以下のように指定することでさまざまな型に対応可能です。

//ジェネリクスを指定
func samplefunc<T>(num1:T, num2:T) -> T {
    return num1 + num2
}

しかし、上記のサンプルを実行するとエラーとなります。

これは関数内でプラス演算子を使用しているため、ジェネリクス型はさまざまな「型」には対応していますが、+などの演算子については対応していないからです。

そのため、ジェネリクス型を指定した関数で演算処理を行う場合は、「関数名<T: Numeric」と指定することで、数値型要素を持つ関数であることを宣言する必要があります。

//ジェネリクスを指定
func samplefunc<T:Numeric>(num1:T, num2:T) -> T {
    return num1 + num2
}

print(samplefunc(num1:100, num2:200)) // 300
print(samplefunc(num1:100.5, num2:200.3)) // 300.8

このプログラムを実行すると、Int型とDouble型の演算が1つの関数で実行できます。

ジェネリクスのクラスを使用する

クラス名の型にT型を指定することにより、クラスのインスタンス生成時にあらゆる型を指定することができます。

以下にジェネリクスを指定したクラスの記述方法を紹介します。

class SampleClass<T> {
    
    var item: T
    
    init(item: T) {
        self.item = item
    }
    
}
 
let cl1 = SampleClass(item: 100)
print(cl1.item)
 
let cl2 = SampleClass(item: "apple")
print(cl2.item)

実行結果:

100
apple

このようにインスタンス生成時にInt型やString型など柔軟に指定できることがわかりますね!

しかしジェネリクス型を指定する場合は、指定した型については意識しておかないと後々の処理で型の不整合がおきたりしますので注意しましょう。

ジェネリックプロトコルの使い方

ジェネリックプロトコル(Generic Protocol)はジェネリクスを使用したプロトコルです。

Protocol(プロトコル)はクラスの型や動作を決めておくための設計図のようなものです。

Swiftではプロトコルにもジェネリクスを使用することが可能です。

以下にプロトコルを定義し、Int型、String型のクラスを定義しています。

//プロトコルの定義
protocol FruitsProtocol {
    associatedtype T
    var item:T { get }     
    func fruitvalue(item:T) 
}


//クラス(Int型)
class Fruits1:FruitsProtocol {

    var item:Int = 100
    
    func fruitvalue(item: Int) {
        self.item += 100
    }
}

//クラス(String型)
class Fruits2:FruitsProtocol {

    var item:String = "100"
    
    func fruitvalue(item: String) {
        self.item = item + "円です"
    }
}

プロトコルでジェネリクスを使用するためには、定義内で「associatedtype T」のように任意の型を指定する必要があります。

プロトコルについて詳しく知りたい方は以下の記事を参考にしてください

まとめ

ここではSwiftのジェネリクス(generics)について、

・ジェネリクス(generics)とは
・ジェネリクスの考え方や使い方
・ジェネリクスのクラスを使用する方法
・ジェネリックプロトコルの使い方

などについて解説しました。

ジェネリクスはSwift特有の機能で、他言語からSwiftを学ぶ場合とっつきにくいイメージもあります。

しかしジェネリクスを使用すれば、型を気にせずにさまざまな処理を行うことができますので非常に便利です。

もしジェネリクスについて忘れてしまったら、この記事を思い出してくださいね!

この記事を書いた人

侍エンジニア塾は「人生を変えるプログラミング学習」をコンセンプトに、過去多くのフリーランスエンジニアを輩出したプログラミングスクールです。侍テック編集部では技術系コンテンツを中心に有用な情報を発信していきます。

目次