协议可以使用关键字定义关联的类型需求associatedtype:
protocol Container { associatedtype Element var count: Int { get } subscript(index: Int) -> Element { get set } }
具有相关类型要求的协议只能用作通用约束:
// These are NOT allowed, because Container has associated type requirements: func displayValues(container: Container) { ... } class MyClass { let container: Container } // > error: protocol 'Container' can only be used as a generic constraint // > because it has Self or associated type requirements // 这些是允许的: func displayValues<T: Container>(container: T) { ... } class MyClass<T: Container> { let container: T }
associatedtype通过在协议期望associatedtype出现的地方提供给定类型,符合协议的类型可以隐式满足要求:
struct ContainerOfOne<T>: Container { let count = 1 // 满足计数要求 var value: T // 隐式满足下标关联类型要求, // 通过将下标分配/返回类型定义为T // 因此Swift会推断出T == Element subscript(index: Int) -> T { get { precondition(index == 0) return value } set { precondition(index == 0) value = newValue } } } let container = ContainerOfOne(value: "Hello")
(请注意,为使本示例更加清楚,将命名通用占位符类型T–更合适的名称为Element,它将掩盖协议的名称associatedtype Element。编译器仍会推断出通用占位符Element用于满足associatedtype Element要求。)
一个associatedtype也可以通过使用一个明确的满足typealias:
struct ContainerOfOne<T>: Container { typealias Element = T subscript(index: Int) -> Element { ... } // ... }
扩展也是如此:
// 公开一个8位整数作为布尔值的集合(每一位一个)。 extension UInt8: Container { // 如上所述,可以推断出这种类型别名 typealias Element = Bool var count: Int { return 8 } subscript(index: Int) -> Bool { get { precondition(0 <= index && index < 8) return self & 1 << UInt8(index) != 0 } set { precondition(0 <= index && index < 8) if newValue { self |= 1 << UInt8(index) } else { self &= ~(1 << UInt8(index)) } } } }
如果符合类型已经满足要求,则不需要实现:
extension Array: Container {} // 数组满足所有要求,包括元素