ジェネリクス(一般化型)は、柔軟で再利用可能な関数や型を定義することを可能にします。Swiftの重要な特徴は、タイプセーフティを保持することです:コンパイラはコンパイル時に特定の型での操作をチェックします。ジェネリック型は、where条件、プロトコルからの継承、およびそれらの組み合わせによって制限できます。
コード例:
func swapValues<T>(_ a: inout T, _ b: inout T) { let temp = a a = b b = temp } protocol Drawable { func draw() } func drawAll<T: Drawable>(_ items: [T]) { for item in items { item.draw() } } // プロトコルとwhere条件による制約 func compareValues<T: Equatable>(_ a: T, _ b: T) -> Bool { return a == b }
ジェネリック関数はオーバーロード関数として機能できますか?コンパイラはどの実装を選択しますか?
回答: はい、ジェネリック関数は通常の関数や他のジェネリック関数とオーバーロードできます。コンパイラは最も特異な実装を選ぼうとします。例:
func printValue(_ value: Int) { print("Int: \(value)") } func printValue<T>(_ value: T) { print("Generic: \(value)") } printValue(5) // Int: 5 printValue("Swift") // Generic: Swift
ストーリー
チームは配列のインデックスを探すためのジェネリック拡張機能を書きましたが、Equatableで型を制限するのを忘れました。その結果、Equatableでない要素を持つ配列にextensionを適用しようとしたときにコンパイルエラーが発生しました。
ストーリー
プロジェクトでは、型制約なしでジェネリックオブジェクトのキャッシュを実装しようとしました。その結果、ランタイムでダウンキャストしようとしたときにクラッシュが発生しました - 安全な実装はassociated typeを持つプロトコルと制約を通じて達成できました。
ストーリー
開発者はジェネリッククラスを実装しましたが、継承とオーバーライドの際に、継承者の完全なジェネリックパラメータを指定する必要があることを忘れました。そのため、コードはコンパイルされず、型階層全体の再設計が必要でした。