Generic — 汎用型の本質とTパラメータ

著者: IT Sectr 公開日: 2026-06-18 読了時間: 10 分

Generic (汎用プログラミング) — Swiftの機構で、パラメータ化された型を使って型や関数を書くことができます。Int、String、カスタム構造体ごとにコードを複製する代わりに、開発者はあらゆる型で動作する単一の汎用ソリューションを作成し、厳密な型付けを維持します。コンパイラは使用時に具体的な型を代入し、パフォーマンスを損なわずに安全性を保証します。Swift Book, 2025によると、汎用パラメータは言語の主要機能のひとつであり、標準ライブラリとSwiftUIの基礎をなしています。

ポイント

  • Generic — 型パラメータを使った汎用プログラミング機構
  • 関数は汎用パラメータを使ってコードを複製せずにあらゆる型で動作する
  • where制約は汎用型の範囲を条件を満たす型に限定する
  • Associated Typesはプロトコルを汎用型にすることを可能にする
  • コンパイラは各具体的な型に対して専用コードを生成する

SwiftでのGenericとは

Generic (汎用型) — Swiftの構文構造で、型をパラメータ化して柔軟かつ再利用可能なコードを書くことができます。具体的な型の代わりに、角括弧にプレースホルダーを書きます—通常はですが、何か別の名前を使うこともできます。GenericsはSwiftUI (View, some View)、標準ライブラリ (Array, Optional)、Combineの基礎となっています。

汎用型が必要な理由

汎用型がなければ、型ごとに別々の関数を書かなければなりません: swapIntsswapStringsswapDates。汎用のswap関数はこれら3つのバリエーションを1つの宣言で置き換えます。これにより、コードの重複が減り、可読性が向上し、コードのコピー時のエラーが減ります。

swift
func swapValues<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

var x = 10
var y = 20
swapValues(&x, &y)

Swiftコンパイラは汎用型の各具体的な使用例に対して専用コードを生成します。すなわち、汎用関数swapValuesはInt用に手書きされた関数と比較してオーバーヘッドがありません。SwiftはJavaのように型イレーションを使いません—汎用型はコンパイル時と実行時の両方に存在します(最適化によって専用化されることもありますが)。

パラメータTを使った汎用関数

汎用関数は、1つ以上の型パラメータを受け取る関数です。型パラメータは関数名の後ろに角括弧で指定します: 。これらはシグネチャ(引数の型、戻り値の型、関数体内部)で使用できます。

複数の型パラメータ

関数は複数の汎用パラメータを持つことができます。例えば、findKey関数はキーKと値Vを持つ辞書を受け取ります。各型パラメータは固有で、制約で使用できます。

swift
func findKey<K: Hashable, V>(
    for value: V,
    in dictionary: [K: V]
) -> K? where V: Equatable {
    for (key, dictValue) in dictionary {
        if dictValue == value {
            return key
        }
    }
    return nil
}

制約K: Hashableとwhere V: Equatableにより、findKeyはキーがハッシュ可能で値が比較可能な辞書でのみ呼べるようになります。こうした制約は形式的なものではなく必須です: Hashableがなければキーの検索をインデックスできず、EquatableがなければvalueとdictValueを比較できません。

whereによる型の制約

where — Swiftのキーワードで、汎用パラメータに追加の制約を課します。単に角括弧 ()でプロトコルを指定するのと違い、whereはより複雑な条件を表現できます: 複数プロトコルへの適合、型パラメータ間の関係、関連型の明確化など。

where構文

where句は関数または型のシグネチャの後、開き括弧の前に書きます。whereではT: Comparable & Hashable(同時に2つのプロトコルに適合)やT.U == Int(関連型の明確化)を指定できます。

swift
protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
}

extension Container where Item: Comparable {
    func isSorted() -> Bool {
        // Implementation available only if Item: Comparable
        return true
    }
}

where付きの拡張は、条件を満たす型だけにメソッドを追加します。ContainerはメソッドisSortedを得ますが、Containerは得ません。AnyはComparableではないからです。これは条件付き機能の強力な機構です。

プロトコルでのAssociated Types

Associated Types (関連型) — 宣言時に具体的な型を指定せずに、プロトコルを汎用型にする方法です。の代わりに、プロトコルはassociatedtypeを宣言し、これが実装で具体化されます。これは配列にとって特に重要です: Sequenceプロトコルはシーケンスが何の要素を含むかを知りません。

whereによるAssociated Typesの関連付け

whereを使って、異なるプロトコルのassociated typesの間に関係を設定できます。例えば、あるプロトコルのItemが別のプロトコルのIterator.Elementと一致することを要求できます。これにより、コンパイルレベルで型の互換性が保証されます。

swift
protocol StackProtocol {
    associatedtype Element
    mutating func push(_ item: Element)
    mutating func pop() -> Element?
}

struct IntStack: StackProtocol {
    typealias Element = Int
    private var items: [Int] = []
    
    mutating func push(_ item: Int) { items.append(item) }
    mutating func pop() -> Int? { items.popLast() }
}

IntStackはtypealiasを使ってElementをIntとして具体化します。コンパイラはこの型でプロトコルのすべての要件を満たしているかを確認します。associated typesがなければ、汎用プロトコルを使ってStackProtocolを書く必要がありますが、SwiftはObjective-Cとの互換性と読みやすさのためにassociated typesを使用します。

拡張とサブスクリプトでのGeneric

汎用拡張により、追加の制約を付けて汎用型にメソッドを追加できます。これは「条件付き適合」パターンで、型が特定の条件でのみ機能を得るものです。サブスクリプトも汎用型にできます。

汎用サブスクリプト

サブスクリプトは汎用型にできます: 角括弧で汎用パラメータを受け取ります。これは、インデックスが異なる型になり得る場合に、任意のインデックスを使った配列への安全なアクセスに役立ちます。

swift
extension Array where Element: Numeric {
    func sum() -> Element {
        return reduce(0, +)
    }
}

extension Array {
    subscript<Indices: Sequence>(indices: Indices) -> [Element]
        where Indices.Element == Int {
        return indices.map { self[$0] }
    }
}

この構造は強力な組み合わせを提供します: sumメソッドは数値配列にのみ表示され、任意のインデックスを使ったサブスクリプトは整数インデックスのあらゆるシーケンスで動作します。コンパイラはwhere条件に基づいてこれらのメンバーの可視性を管理します。

汎用型を使う際の一般的な誤り

誤りは、汎用型の使用において、間違った制約や、具体的な型が必要な場所で汎用型を使おうとすることによって生じます。開発者がよく直面する3つのシナリオを見てみましょう。

過度なパラメータ化

具体的な型で十分な場所で汎用パラメータを追加するのはアンチパターンです。ある関数が常にStringを扱う場合、それをにする必要はありません。これはコードを複雑にするだけで利益はありません。汎用型は型が正に変わる場合にのみ証明されます。

where制約の缺如

メソッドが存在することを指定せずに、汎用パラメータでメソッドを呼ぶのはエラーです。がT: Collectionを指定せずに.countを呼ぶことはできません。すべての型パラメータに制約を追加する必要があります。さもなければコンパイラはコードを拒否します。

swift
// ❌ Error: value has no count method
func countElements<T>(value: T) -> Int { value.count }

// ✅ Works: T: Collection guarantees count exists
func countElements<T: Collection>(value: T) -> Int { value.count }

エラーは明確です: 汎用あらゆる型になり得るため、コンパイラはそれがcountを持つかどうかを知りません。Collection制約が問題を解決します。Equatable、Hashable、Numericについても同様です—汎用パラメータでメソッドや演算子を呼ぶ計画があれば、常にプロトコルを指定してください。

よくある質問

簡単に言うとSwiftでのGenericとは何ですか?

Genericとは、Int、String、その他の型について同じ論理を繰り返すことなく、あらゆる型で動作するコードを書く方法です。具体的な型の代わりにプレースホルダーを使用し、使用時に実際の型で置き換えられます。

GenericはAnyと何が違いますか?

Genericはコンパイル時に具体的な型の情報を保持し、コンパイラが型をチェックし、最適化されたコードを生成できます。Anyは型消しです: どんな値でも渡せますが、型が失われ、キャスト(as?)が必要です。

プロトコルでGenericを制約するにはどうすればいいですか?

角括弧でT: SomeProtocol構文か、別途のwhere句where T: SomeProtocolを使用します。制約により、汎用パラメータが特定のメソッドやプロパティをサポートすることが保証されます。

プロトコルでのAssociated Typeとは何ですか?

Associated Type (associatedtype)はプロトコル内の型のプレースホルダーです。プロトコルは具体的な型を指定せずにassociatedtypeを宣言し、実装で具体化されます: ArrayはElement、DictionaryはKeyとValueを持ちます。

Genericは具体的な型より速いですか、遅いですか?

Swiftは汎用コードを各具体的な型に対して専用バージョンにコンパイルします。実際には、Int用の汎用関数はInt用に手書きされた関数より遅くはありません。抽象化によるオーバーヘッドはありません。

まとめ

  • Generic — 型パラメータを使った汎用プログラミング機構
  • 関数と型は汎用パラメータを使って複数の重複実装を置き換える
  • where制約は汎用型の範囲を必要な条件に限定する
  • Associated Typesはプロトコルを宣言時に具体化せずに汎用型にできるようにする
  • 拡張はwhereにより条件を満たす場合にのみ機能を追加する
  • コンパイラは専用コードを生成する—汎用型によるオーバーヘッドはない
  • 過度なパラメータ化はアンチパターン—型が正に変わる場合にのみ汎用型を使う

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください