Swift4语法极简手册 — Generics

Swift4语法极简手册

第二十三章节:Generics

Generics

generics可以让你写出可灵活,重用度更高的代码

我们来看下以下的场景

1
2
3
4
5
6
//交换两个Int值
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}

如类似上面的场景,我们可能想交换两个Int值,也可能想交换两个Double值,或任何其它Type

这个时候,我们就需要用到Generics

1
2
3
4
5
6
//用Generics T来代表某种Type
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}

如上,使用T来代表任意一种Type

你可以使用任意来替换T,如A,B或SomeType都行

通过这种方式,你可以写出适合很多不同Type的通用的代码

写一个Generic Type类

1
2
3
4
5
6
7
8
9
10
//写一个Stack,用Generic
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}

如上代码所示,写一个Stack,因为stack适合任何类,所以我们用Generic来实现

Generic和Any

  • Generic限定为任何Type,但必须属于同一种Type
  • Any不限定Type

Extend Generic Type

给Generic Type写Extend时,你可以直接使用Type中定义的Generic (如T,Element这些)

1
2
3
4
5
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}

Generic Type Constrains

你也可以给Generic Type添加一些限制

1
2
3
4
//T 必须继承SomeClass,U必须实现SomeProtocol
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}

如上代码所示,添加了些约束,只有符合约束的才允许调用

Associated Types

protocol中,是不可以使用Generic的,而是要使用associated types

1
2
3
4
5
6
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}

Container协议,定义了一个associated type对象Item,然后在几个方法中引用了这个Item

当实现这个Protocol,必须遵守Item的一致性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 Item = Int
mutating func append(_ item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}

如上述这个实现,在实现时用Int表示协议中的Item,这样就满足这个协议了

associated type也可以如同Generic一样添加约束

1
2
3
4
5
6
protocol Container {
associatedtype Item: Equatable
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}

如上,约束Item的Type必须实现Equatable协议

Generic Where Clauses

还可以给Generic添加where条件

1
2
3
4
5
6
7
func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {
//TODO
// All items match, so return true.
return true
}

如上,约束C1,C2两个Generic必须满足C1.Item == C2.Item, C1.Item: Equatable

Extend 也可以添加where

1
2
3
4
5
6
7
8
9
extension Container where Item == Double {
func average() -> Double {
var sum = 0.0
for index in 0..<count {
sum += self[index]
}
return sum / Double(count)
}
}

associate type也可以添加where

1
2
3
4
5
6
7
8
9
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
associatedtype Iterator: IteratorProtocol where Iterator.Element == Item
func makeIterator() -> Iterator
}

Generic Subscripts也可以添加where

1
2
3
4
5
6
7
8
9
10
extension Container {
subscript<Indices: Sequence>(indices: Indices) -> [Item]
where Indices.Iterator.Element == Int {
var result = [Item]()
for index in indices {
result.append(self[index])
}
return result
}
}

如上,Indices是一个Generic,需要实现Sequence,并且添加了一个where约束 Indices.Iterator.Element == Int