プロトコル合成は、Swiftで複数のプロトコルに同時に準拠するタイプを作成できるメカニズムです。これは、クラスに対する複数の継承の代替手段です。
背景
Objective-Cは、クラスではなくプロトコルに対してのみ複数の継承をサポートしていました。Swiftはこの伝統を引き継ぎ、プロトコルとその組み合わせを使用して新しい抽象を構築することに重点を置いています。
問題
プログラマーは、しばしば複数の抽象によって定義される動作を持つタイプを作成する必要があります。複数の継承は必然的に階層の競合を引き起こしますが、Swiftではプロトコルとプロトコル合成によって安全に解決されます。
解決策
Swiftでは、'&'オペレーターを使用してプロトコルを組み合わせることができます。これにより、複数のプロトコルに同時に準拠する必要がある変数や関数のパラメータを作成できます。
コードの例:
protocol Drivable { func drive() } protocol Flyable { func fly() } struct FlyingCar: Drivable, Flyable { func drive() { print("Driving") } func fly() { print("Flying") } } func testVehicle(_ vehicle: Drivable & Flyable) { vehicle.drive() vehicle.fly() } testVehicle(FlyingCar())
主な特徴:
プロトコル合成のパラメータに、いずれかのプロトコルのみを実装するオブジェクトを渡すことはできますか?
いいえ、オブジェクトは合成に参加するすべてのプロトコルを実装している必要があります。そうでない場合、コンパイラはエラーを出します。
コードの例:
// struct Car: Drivable {} — testVehicleに渡せません、なぜならfly()が実装されていないから
プロトコル合成はタイプに適用されますか、それとも値だけですか?
プロトコル合成は値(変数、関数のパラメータ)に適用されますが、オブジェクトの型定義には使用されません(例えば、新しい型を「プロトコルの合成」として宣言することはできない — 変数だけです)。
コードの例:
var obj: SomeProtocol & AnotherProtocol // 許可されます // typealias MyType = SomeClass & AnotherProtocol // エラー
&を使ってクラスとプロトコルを組み合わせることはできますか?
はい、ただし1つの制限があります。左側にはクラス型(クラスまたはそのサブクラス)だけが許可され、他はすべてプロトコルでなければなりません。そうでない場合、コンパイラはエラーを出します。
コードの例:
class A {} protocol B {} // func f(obj: A & B) {} // 許可されます // func f(obj: A & AnotherClass & B) {} // エラー!クラス型は1つだけが許可されます
プロジェクト内でレイヤー間のデータを転送するためにプロトコル合成を使用するオブジェクトが使用されていますが、実際には単一のプロトコルで十分でした:
func present(item: Displayable & Serializable) { ... }
利点:
明示的なケースでのみプロトコル合成を使用 — 例えば、CodableとIdentifiableの両方をサポートするオブジェクトを処理する場合:
func save<T: Codable & Identifiable>(_ item: T) { ... }
利点: