【Swift】ジェネリクスの意味と使い方!Comparableプロトコルとは?

この記事からわかること

  • Swiftジェネリクスとは?
  • 意味使い方メリット
  • 関数構造体への組み込み方
  • Comparableプロトコルとは?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

ジェネリクスとは?

ジェネリクスとはSwift言語の仕様の1つで、特定のデータの型に依存しない関数などを定義することができます。Swift内部でもジェネリクスは使用されており、タプルや配列などが異なるデータ型を保持できるのもこのジェネリクスを使用しているからのようです。

ジェネリクスのような仕様は他言語でも実装されている仕様でジェネリックプログラミングとも呼ばれています。generic(:ジェネリック)は日本語で「一般的な」を意味する英単語です。

ジェネリクスの構文と使い方

ジェネリクスは型を定義する様に使用したり、関数名の後に<任意の型名>を使って使用します。型名は任意の名称を付けることができますが、慣習的にTが使われることが多いようです。また任意の際の命名規則はキャメルケース(CamelCase)を用いることが推奨されています。

例えば下記のような引数のデータ型を出力してnilを返す関数を定義するとします。関数名の後にジェネリクスを表す<T>を、引数には2つとも様々なデータ型でも許容できるように同様にTを指定します。今回は返り値にnilを許容できるようにオプショナル型にします。型の制限を設けないジェネリクスですがオプショナル型は許容していないので明示的に?を使ってオプショナル型を許容します。

func judgeType<T> (one: T, two: T) -> T? {
    print("oneのデータ型は\(type(of: one))")
    print("twoのデータ型は\(type(of: two))")
    return nil
}

上記の実行結果

judgeType(one: 6, two: 4000)
// oneのデータ型はInt
// twoのデータ型はInt

judgeType(one: 0.7, two: 4000)
// oneのデータ型はDouble
// twoのデータ型はDouble

judgeType(one: "Swift", two:"UI")
// oneのデータ型はString
// twoのデータ型はString

judgeType(one: "Swift", two: 4000) // パースエラー
expression failed to parse:
error: conflicting arguments to generic parameter 'T' ('Int' vs. 'String')
judgeType(one: "Swift", two:5)

judgeType関数は1つしか定義していませんが、異なるデータ型を引数で受け取ってもエラーになることなく実行することができます。しかし今回のように引数2つともにジェネリクスTを指定した場合、両者のデータ型は同じでなければなりません

またこのようにジェネリクスを用いた関数のことをジェネリクス関数と呼びます。

ジェネリクスのポイント

Comparableプロトコルでジェネリクス関数に比較式を許容する

ジェネリクスを引数に用いている場合に関数の中で比較( = や > など)をしてしまうとエラーになってしまいます。

func equalType<T> (one: T, two: T) -> Bool {
    if one == two {
        return true
    }else{
        return false
    }
}

比較を用いた場合のパースエラー

expression failed to parse:
error: binary operator '==' cannot be applied to two 'T' operands
// エラー:二項演算子'=='は2つの'T'オペランドに適用できません

これを防ぐにはTの後にComparableプロトコルを指定します。

func equalType<T: Comparable> (one: T, two: T) -> Bool {
    if one == two {
        return true
    }else{
        return false
    }
}

equalType(one: 6, two: 6) // true
equalType(one: "SWIFT", two: "swift") // false

エラーになってしまう理由はどんなデータ型でも許容できる代わりに、比較できないデータ型を渡される可能性もあるためです。

Comparableプロトコルは値の比較が可能なデータ型であることをルールとしているプロトコルです。

Comparableプロトコルの定義

public protocol Comparable : Equatable {
    static func < (lhs: Self, rhs: Self) -> Bool
    static func <= (lhs: Self, rhs: Self) -> Bool
    static func >= (lhs: Self, rhs: Self) -> Bool
    static func > (lhs: Self, rhs: Self) -> Bool
}

受ける変数の型によってメソッド内の型を変更する

ジェネリクスを使用して以下のようにメソッド自体には引数などで受け取らず、受け取る変数の型に応じてメソッド内の処理を切り替えることも可能です。

func test<T>() -> T {
    if T.self == Int.self {
        return 10 as! T
    } else if T.self == String.self {
        return "Hello" as! T
    } else {
        fatalError("Unsupported type")
    }
}

let test2:Int = test() // test2はInt型
print(test2) // 10
let test3:String = test() // test3はString型
print(test3) // Hello

構造体にジェネリクスを指定する

構造体にジェネリクスを指定する場合も関数と同様に<T>のように定義します。

struct Box<T>{
    var area1 : T
    var area2 : T
    var key : String
    
}

またプロパティ(変数)にもBox<T>部分で指定した型名でジェネリクスとして指定することができるようになります。

プロトコルへのジェネリックプログラミング

Swiftのプロトコルに対してジェネリックプログラミングな実装をするために用意されているassociatedtypeキーワードが用意されています。

public protocol View {
    associatedtype Body : View
    @ViewBuilder  var body: Self.Body { get }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index