泛型函數
func swapTwoValues<T>(_ a: inout T, _ b: inout T) { let temporaryA = a
a= b
b = temporaryA
}
func swapTwoInts(_ a: inout Int, _ b: inout Int)
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
這個函數的泛型版本使用了佔位類型名來代替實際類型名。佔位類型名沒有指明T必須是什麼類型,但是它指明瞭a和b必須是同一類型T,無論T代表什麼類型,只有swapTwoValues(_:_:)函數在調用時,才能根據傳入的實際類型決定T所代表的類型。var someInt = 3
var anotherInt = 107 swapTwoValues(&someInt, &anotherInt)
// someInt 現在 107, and anotherInt 現在 3
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString 現在 "world", and anotherString 現在 "hello"
泛型類型
struct Stack<Element> {
var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
} }
這些方法被標記爲mutating,因爲它們需要修改結構體的Item數組。擴展一個泛型類型
當你擴展一個泛型類型的時候,並不需要再擴展的定義中提供類型參數列表。原始類型定義彙總聲明的類型參數列表在擴展中可以直接使用,並且這些來自原始類型中的參數名稱會被用作原始定義中類型參數的引用。 extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
計算型屬性現在可以用來訪問任意Stack實例的頂端元素且不移除它: if let topItem = stackOfStrings.topItem {
print("The top item on the stack is \(topItem).")
}
類型約束
func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
} }
return nil }
let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findIndex(ofString: "llama", in: strings) {
print("The index of llama is \(foundIndex)")
}
下面展示的上述的函數的泛型版本 func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
} }
return nil }
上面所寫的函數無法通過編譯。問題出在相等性檢查上,不是所有的swift類型都可以用等式符進行比較。不過,所有的這些並不會讓我們無從下手。swift標準庫中定義了一個Equatable協議,該協議要求任何遵循該協議的類型必須實現等式符及不等符,從而能對該類型的任何兩個值進行比較。所有的swift標準類型自動支持Equatable協議。 func findIndex<T: Equatable>(array: [T], _ valueToFind: T) -> Int? {
for (index, value) in array.enumerate() {
if value == valueToFind {
return index
} }
return nil }
任何符合Equatable協議的類型T都能用這個泛型函數:let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25])
// doubleIndex 類型爲 Int?,其值爲 nil,因爲 9.3 不在數組中
let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"]) // stringIndex 類型爲 Int?,其值爲 2
關聯類型
protocol Container {
associatedtype ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
由於swift的類型推斷,實際上不用再Stack的定義中聲明ItemType爲Int。因爲Stack符合Container協議的所有要求,swift只需通過append(_:)方法的item參數類型和下標返回值的類型,就可以推斷出ItemType的具體類型。實際上,如果在不寫typealias ItemType = Int 這一行,一切仍舊可以正常工作,因爲swift清楚的知道ItemType應該是那種類型:struct Stack<Element>: Container {
// Stack<Element> 的原始實現部分 var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// Container 協議的實現部分
mutating func append(item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
泛型Where語句
爲關聯類型定義約束也是非常有用的。可以在參數列表中通過where子句爲關聯類型定義約束。能通過where子句要求一個關聯類型遵從某個特定的協議,以及某個特定的類型參數和關聯類型必須類型相同。可以通過將where關鍵字跟在類型參數列表後面來定義where子句,where子句後跟一個或者多個針對關聯類型的約束,以及一個或多個類型參數和關聯類型間的相等關係。可以在函數體或者類型的大括號之前添加where子句。func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
// 檢查兩個容器含有相同數量的元素
if someContainer.count != anotherContainer.count {
return false
}
// 檢查每一對元素是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
} }
// 所有元素都匹配,返回 true return true
}
這個函數接受兩個參數,參數someContainer的類型爲C1,參數anotherContainer的類型爲C2。C1和C2是容器的兩個佔位類型參數,函數被調用時才能確定他們的具體類型。 var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
var arrayOfStrings = ["uno", "dos", "tres"]
if allItemsMatch(stackOfStrings, arrayOfStrings) {
print("All items match.")
} else {
print("Not all items match.")
}
// 打印 “All items match.”
取自《the swift programming language》中文版