Swift关联类型
关联类型
当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个节点名(或别名)。作用于关联类型上实际类型是不需要指定的,直到该协议接受。关联类型被指定为typealias
关键字。
关联类型行为
这里是一个Container
协议的例子,定义了一个ItemType关联类型:
protocol Container {
typealias ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
Container
协议定义了三个任何容器必须支持的兼容要求:
-
必须可能通过
append
方法添加一个新item到容器里; -
必须可能通过使用
count
属性获取容器里items的数量,并返回一个Int
值; -
必须可能通过容器的
Int
索引值下标可以检索到每一个item。
这个协议没有指定容器里item是如何存储的或何种类型是允许的。这个协议只指定三个任何遵循Container
类型所必须支持的功能点。一个遵循的类型也可以提供其他额外的功能,只要满足这三个条件。
任何遵循Container
协议的类型必须指定存储在其里面的值类型,必须保证只有正确类型的items可以加进容器里,必须明确可以通过其下标返回item类型。
为了定义这三个条件,Container
协议需要一个方法指定容器里的元素将会保留,而不需要知道特定容器的类型。Container
协议需要指定任何通过append
方法添加到容器里的值和容器里元素是相同类型,并且通过容器下标返回的容器元素类型的值的类型是相同类型。
为了达到此目的,Container
协议声明了一个ItemType的关联类型,写作typealias ItemType
。这个协议不会定义ItemType
是什么的别名,这个信息留给了任何遵循协议的类型来提供。尽管如此,ItemType
别名支持一种方法识别在一个容器里的items类型,以及定义一种使用在append
方法和下标中的类型,以便保证任何期望的Container
的行为是强制性的。
这里是一个早前IntStack类型的非泛型版本,适用于遵循Container协议:
struct IntStack: Container {
// original IntStack implementation
var items = Int[]()
mutating func push(item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// conformance to the Container protocol
typealias ItemType = Int
mutating func append(item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
IntStack
类型实现了Container
协议的所有三个要求,在IntStack
类型的每个包含部分的功能都满足这些要求。
此外,IntStack
指定了Container
的实现,适用的ItemType被用作Int
类型。对于这个Container
协议实现而言,定义 typealias ItemType = Int
,将抽象的ItemType
类型转换为具体的Int
类型。
感谢Swift类型参考,你不用在IntStack
定义部分声明一个具体的Int
的ItemType
。由于IntStack
遵循Container
协议的所有要求,只要通过简单的查找append
方法的item参数类型和下标返回的类型,Swift就可以推断出合适的ItemType
来使用。确实,如果上面的代码中你删除了typealias ItemType = Int
这一行,一切仍旧可以工作,因为它清楚的知道ItemType使用的是何种类型。
你也可以生成遵循Container
协议的泛型Stack
类型:
struct Stack<T>: Container {
// original Stack<T> implementation
var items = T[]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
// conformance to the Container protocol
mutating func append(item: T) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> T {
return items[i]
}
}
这个时候,占位类型参数T
被用作append
方法的item参数和下标的返回类型。Swift 因此可以推断出被用作这个特定容器的ItemType
的T
的合适类型。
扩展一个存在的类型为一指定关联类型
在使用扩展来添加协议兼容性中有描述扩展一个存在的类型添加遵循一个协议。这个类型包含一个关联类型的协议。
Swift的Array
已经提供append
方法,一个count
属性和通过下标来查找一个自己的元素。这三个功能都达到Container
协议的要求。也就意味着你可以扩展Array
去遵循Container
协议,只要通过简单声明Array
适用于该协议而已。如何实践这样一个空扩展,在使用扩展来声明协议的采纳中有描述这样一个实现一个空扩展的行为:
extension Array: Container {}
如同上面的泛型Stack
类型一样,Array的append
方法和下标保证Swift
可以推断出ItemType
所使用的适用的类型。定义了这个扩展后,你可以将任何Array
当作Container
来使用。