swiftでprotocolを汎化しようとした場合、以下のように定義が出来ます
protocol Entity {
associatedtype IdentityType
var identity:IdentityType { get }
}
Entityであるのならば、identityという何らかの型の変数を持っていると定義しています。
ここでさらにIdentityTypeに型制約を付たい場合、protocolでは以下のように定義します。
protocol Entity {
associatedtype IdentityType: Equatable
var identity:IdentityType { get }
}
Genericsに型制約を付けるのと同じ記述をtypealiasにしてあげるだけですね。
複数の制約をつける事も出来ます
protocol Entity {
associatedtype IdentityType: Equatable, Comparable
var identity:IdentityType { get }
}
本題からは少し外れますが、このような制約をつけていくとprotocolの性質を明確に出来る他に、Genericsの関数を作ってコードの量を減らせるというメリットも出てきます
例えば、EntityへEquatableの制約を付け足した場合
protocol Entity: Equatable {
associatedtype IdentityType: Equatable, Comparable
var identity:IdentityType { get }
}
func == <T:Entity> (lhs : T, rhs : T) -> Bool {
return lhs.identity == rhs.identity
}
上記のようなEntityを受け取る関数を一つ定義してあげれば良いようになります。
(Identityを比較すれば良いという条件ではあるけれど、)
実際にクラスを実装する場合はこのようになります
例えばInt
をidentityにすると、Int
はEquatable
とComparable
を実装しているのでコンパイルが通ります
// OK
class MyEntity : Entity {
// 補足: getがあれば良いのでletでもvarでも良い
let identity: Int = 0
}
Comparable
とEquatable
を実装していない型をIdentityにするとエラーになります
// Fail Type MyEntity does not conform to protocol `Entity`
class MyEntity : Entity {
let identity: NantokaType = NantokaType()
}
Genericsはうまい事使えば型安全を保って無駄のないコードが書けそうなので、もっと活用していきたいですね。