1、泛型:使用泛型能更清晰更簡潔的表達代碼意圖
// 泛型所解決的問題
func swapTwoInts(a: inout Int, _ b: inout Int) { // 交換兩個Int的值
let temp = a
a = b
b = temp
}
var someInt = 3
var anotherInt = 5
swapTwoInts(a: &someInt, &anotherInt)
// 但如果想交換String Double類型的數該怎樣做呢?再寫兩個函數?
2、泛型函數:在函數名后面添加<T>,表示T是函數定義的占位類型;只要前后一致,別的字母也可以
func swapTwoValues<T>(a: inout T, _ b: inout T) { // 用T來替代實際類型,a和b是同一類型T
let temp = a
a = b
b = temp
}
swapTwoValues(a: &someInt, &anotherInt) // 在調用函數時,根據實際類型推斷出T的類型
var StringA = "A"
var StringB = "B"
swapTwoValues(a: &StringA, &StringB) // 只要傳入任何相同的類型都可以
print("StringA:(StringA),StringB:(StringB)") // StringA:B,StringB:A
3、類型參數
// 在上面的例子中占位類型T就是一個類型參數.
// 類型參數可以在函數名后面加一個占位類型, 并用尖括號括起來.例如< T >
// 類型參數設定后,就可以當做函數的參數類型使用了,可以是參數類型也可以是返回類型
// 當函數被調用的時候,類型參數會被轉化為實際類型
// 類型參數可以有多個,可以寫在尖括號中,用逗號隔開
// 通常使用單個字母T U V來命名類型參數,但也可以是以大寫字母開頭的字符串
4、泛型類型:能夠讓自定義類、結構體、和枚舉適用于任何類型,類似于Array和Dictionary
struct Stack<Element> { // 模擬棧的操作過程的泛型集合類型
var items = Element // 使用Element為空數組進行初始化
mutating func push(item: Element) { // push的參數類型是Element
items.append(item)
}
mutating func pop() -> Element { // pop的返回值類型是Element類型
return items.removeLast()
}
}
var stackOfStrings = Stack<String>() // 在尖括號中寫出棧中需要存儲的數據類型
stackOfStrings.push(item: "A")
stackOfStrings.push(item: "B")
stackOfStrings.push(item: "C")
let fromTheTop = stackOfStrings.pop()
print(stackOfStrings.items) // ["A", "B"]
5、泛型類型的擴展:原類型中的類型參數在擴展中可以直接使用
extension Stack {
var topItem: Element? { // 返回棧頂元素的只讀計算屬性
return items.isEmpty ? nil : items[items.count - 1]
}
}
if let topItem = stackOfStrings.topItem {
print("topItem:(topItem)") // topItem:B
}
6、類型約束:swapTwoValues(::) 和Stack適用于任何類型,但有時我們需要對類型進行一些約束,這些約束可以是類型參數必須繼承指定類,或者符合特定的協議或協議組合
class SomeClass {}
protocol SomeProtocol {}
// T的類型參數必須是SomeClass的子類,U的類型參數必須遵守SomeProtocol協議
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { // 類型約束語法
}
// 根據字符串查找在數組中的索引
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
// 為T增加一個Equatable協議,遵循該協議的類型必須實現等式符和不等符號.才可以對兩個類型進行比較
if value == valueToFind {
return index
}
}
return nil
}
let doubleIndex = findIndex(of: 5.0, in: [3.2, 1.0, 5.0, 5,6]) // 2
let stringIndex = findIndex(of: "O", in: ["V", "D", "E", "O"]) // 3
7、類型的關聯:關聯類型為協議中某個類型提供別名
protocol Container { // 定義一個Container協議
associatedtype ItemType // 通過associatedtype關鍵詞來定義一個關聯類型
mutating func append(item: ItemType) // 添加一個新的元素到容器里
var count: Int { get } // 獲取容器中的元素個數
subscript(i: Int) -> ItemType { get } // 通過索引獲取容器中的元素
}
struct NewStack<Element>: Container {
var items = Element
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
mutating func append(item: Element) {
self.push(item: item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element { // swift會自動推斷Element就是ItemType類型
return items[i] // 不需要再去指定ItemType類型為Element類型
}
}
8、where子句:為泛型的類型參數做一些約束
protocol Container { // 定義一個Container協議
associatedtype ItemType // 通過associatedtype關鍵詞來定義一個關聯類型
mutating func append(item: ItemType) // 添加一個新的元素到容器里
var count: Int { get } // 獲取容器中的元素個數
subscript(i: Int) -> ItemType { get } // 通過索引獲取容器中的元素
}
struct NewStack<Element>: Container {
var items = Element
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
mutating func append(item: Element) {
self.push(item: item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element { // swift會自動推斷Element就是ItemType類型
return items[i] // 不需要再去指定ItemType類型為Element類型
}
}