# Swift 泛型
Swift 提供了泛型讓你寫出靈活且可重用的函數和類型。
Swift 標準庫是通過泛型代碼構建出來的。
Swift 的數組和字典類型都是泛型集。
你可以創建一個Int數組,也可創建一個String數組,或者甚至于可以是任何其他 Swift 的類型數據數組。
以下實例是一個非泛型函數 exchange 用來交換兩個 Int 值:
```
// 定義一個交換兩個變量的函數
func exchange(inout a: Int, inout b: Int) {
let temp = a
a = b
b = temp
}
var numb1 = 100
var numb2 = 200
print("交換前數據: \(numb1) 和 \(numb2)")
exchange(&numb1, b: &numb2)
print("交換后數據: \(numb1) 和 \(numb2)")
```
以上程序執行輸出結果為:
```
交換前數據: 100 和 200
交換后數據: 200 和 100
```
泛型函數可以訪問任何類型,如 Int 或 String。
以下實例是一個泛型函數 exchange 用來交換兩個 Int 和 String 值:
```
func exchange<T>(inout a: T, inout b: T) {
let temp = a
a = b
b = temp
}
var numb1 = 100
var numb2 = 200
print("交換前數據: \(numb1) 和 \(numb2)")
exchange(&numb1, b: &numb2)
print("交換后數據: \(numb1) 和 \(numb2)")
var str1 = "A"
var str2 = "B"
print("交換前數據: \(str1) 和 \(str2)")
exchange(&str1, b: &str2)
print("交換后數據: \(str1) 和 \(str2)")
```
以上程序執行輸出結果為:
```
交換前數據: 100 和 200
交換后數據: 200 和 100
交換前數據: A 和 B
交換后數據: B 和 A
```
這個函數的泛型版本使用了占位類型名字(通常此情況下用字母T來表示)來代替實際類型名(如Int、String或Double)。占位類型名沒有提示T必須是什么類型,但是它提示了a和b必須是同一類型T,而不管T表示什么類型。只有 exchange(_:_:)函數在每次調用時所傳入的實際類型才能決定T所代表的類型。
另外一個不同之處在于這個泛型函數名后面跟著的占位類型名字(T)是用尖括號括起來的(<t>)。這個尖括號告訴 Swift 那個T是 exchange(_:_:)函數所定義的一個類型。因為T是一個占位命名類型,Swift 不會去查找命名為T的實際類型。</t>
## 泛型類型
Swift 允許你定義你自己的泛型類型。
自定義類、結構體和枚舉作用于任何類型,如同Array和Dictionary的用法。
```
struct TOS<T> {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
var tos = TOS<String>()
tos.push("Swift")
print(tos.items)
tos.push("泛型")
print(tos.items)
tos.push("類型參數")
print(tos.items)
tos.push("類型參數名")
print(tos.items)
let deletetos = tos.pop()
```
以上程序執行輸出結果為:
```
["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "類型參數"]
["Swift", "泛型", "類型參數", "類型參數名"]
```
## 擴展泛型類型
當你擴展一個泛型類型的時候(使用 extension 關鍵字),你并不需要在擴展的定義中提供類型參數列表。更加方便的是,原始類型定義中聲明的類型參數列表在擴展里是可以使用的,并且這些來自原始類型中的參數名稱會被用作原始定義中類型參數的引用。
### 實例
```
struct TOS<T> {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
var tos = TOS<String>()
tos.push("Swift")
print(tos.items)
tos.push("泛型")
print(tos.items)
tos.push("類型參數")
print(tos.items)
tos.push("類型參數名")
print(tos.items)
// 擴展泛型 TOS 類型
extension TOS {
var first: T? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
if let first = tos.first {
print("棧頂部項:\(first)")
}
```
以上程序執行輸出結果為:
```
["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "類型參數"]
["Swift", "泛型", "類型參數", "類型參數名"]
棧頂部項:類型參數名
```
## 類型約束
類型約束指定了一個必須繼承自指定類的類型參數,或者遵循一個特定的協議或協議構成。
### 類型約束語法
你可以寫一個在一個類型參數名后面的類型約束,通過冒號分割,來作為類型參數鏈的一部分。這種作用于泛型函數的類型約束的基礎語法如下所示(和泛型類型的語法相同):
```
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 這里是函數主體
}
```
### 實例
```
// 函數可以作用于查找一字符串數組中的某個字符串
func findStringIndex(array: [String], _ valueToFind: String) -> Int? {
for (index, value) in array.enumerate() {
if value == valueToFind {
return index
}
}
return nil
}
let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findStringIndex(strings, "llama") {
print("llama 的下標索引值為 \(foundIndex)")
}
```
以上程序執行輸出結果為:
```
llama 的下標索引值為 2
```
## 關聯類型實例
Swift 中使用 typealias 關鍵字來設置關聯類型。
定義一個協議時,有的時候聲明一個或多個關聯類型作為協議定義的一部分是非常有用的。
```
protocol Container {
// 定義了一個ItemType關聯類型
typealias ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
// 遵循Container協議的泛型TOS類型
struct TOS<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]
}
}
var tos = TOS<String>()
tos.push("Swift")
print(tos.items)
tos.push("泛型")
print(tos.items)
tos.push("參數類型")
print(tos.items)
tos.push("類型參數名")
print(tos.items)
```
以上程序執行輸出結果為:
```
["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "參數類型"]
["Swift", "泛型", "參數類型", "類型參數名"]
```
## Where 語句
類型約束能夠確保類型符合泛型函數或類的定義約束。
你可以在參數列表中通過where語句定義參數的約束。
你可以寫一個where語句,緊跟在在類型參數列表后面,where語句后跟一個或者多個針對關聯類型的約束,以及(或)一個或多個類型和關聯類型間的等價(equality)關系。
### 實例
下面的例子定義了一個名為allItemsMatch的泛型函數,用來檢查兩個Container實例是否包含相同順序的相同元素。
如果所有的元素能夠匹配,那么返回一個為true的Boolean值,反之則為false。
```
protocol Container {
typealias ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
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]
}
}
func allItemsMatch<
C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, anotherContainer: C2) -> Bool {
// 檢查兩個Container的元素個數是否相同
if someContainer.count != anotherContainer.count {
return false
}
// 檢查兩個Container相應位置的元素彼此是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// 匹配所有項,返回 true
return true
}
var tos = Stack<String>()
tos.push("Swift")
print(tos.items)
tos.push("泛型")
print(tos.items)
tos.push("Where 語句")
print(tos.items)
var eos = ["Swift", "泛型", "Where 語句"]
print(eos)
```
以上程序執行輸出結果為:
```
["Swift"]
["Swift", "泛型"]
["Swift", "泛型", "Where 語句"]
["Swift", "泛型", "Where 語句"]
```
- Swift 簡介
- Swift 環境搭建
- Swift 基本語法
- Swift 數據類型
- Swift 變量
- Swift 可選(Optionals)類型
- Swift 常量
- Swift 字面量
- Swift 運算符
- Swift 條件語句
- Swift if 語句
- Swift if...else 語句
- Swift if...else if...else 語句
- Swift 嵌套 if 語句
- Swift switch 語句
- Swift 循環
- Swift for-in 循環
- Swift for 循環
- Swift While 循環
- Swift repeat...while 循環
- Swift Continue 語句
- Swift Break 語句
- Swift Fallthrough 語句
- Swift 字符串
- Swift 字符(Character)
- Swift 數組
- Swift 字典
- Swift 函數
- Swift 閉包
- Swift 枚舉
- Swift 結構體
- Swift 類
- Swift 屬性
- Swift 方法
- Swift 下標腳本
- Swift 繼承
- Swift 構造過程
- Swift 析構過程
- Swift 可選鏈
- Swift 自動引用計數(ARC)
- Swift 類型轉換
- Swift 擴展
- Swift 協議
- Swift 泛型
- Swift 訪問控制