Swift類型約束
類型約束
swapTwoValues
函數和Stack
類型可以作用於任何類型,不過,有的時候對使用在泛型函數和泛型類型上的類型強制約束爲某種特定類型是非常有用的。類型約束指定了一個必須繼承自指定類的類型參數,或者遵循一個特定的協議或協議構成。
例如,Swift 的Dictionary
類型對作用於其鍵的類型做了些限制。在字典的描述中,字典的鍵類型必須是可哈希,也就是說,必須有一種方法可以使其被唯一的表示。Dictionary
之所以需要其鍵是可哈希是爲了以便於其檢查其是否已經包含某個特定鍵的值。如無此需求,Dictionary
既不會告訴是否插入或者替換了某個特定鍵的值,也不能查找到已經存儲在字典裏面的給定鍵值。
這個需求強制加上一個類型約束作用於Dictionary
的鍵上,當然其鍵類型必須遵循Hashable
協議(Swift 標準庫中定義的一個特定協議)。所有的 Swift 基本類型(如String
,Int
, Double
和 Bool
)默認都是可哈希。
當你創建自定義泛型類型時,你可以定義你自己的類型約束,當然,這些約束要支持泛型編程的強力特徵中的多數。抽象概念如可哈希
具有的類型特徵是根據它們概念特徵來界定的,而不是它們的直接類型特徵。
類型約束語法
你可以寫一個在一個類型參數名後面的類型約束,通過冒號分割,來作爲類型參數鏈的一部分。這種作用於泛型函數的類型約束的基礎語法如下所示(和泛型類型的語法相同):
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
上面這個假定函數有兩個類型參數。第一個類型參數T
,有一個需要T
必須是SomeClass
子類的類型約束;第二個類型參數U
,有一個需要U
必須遵循SomeProtocol
協議的類型約束。
類型約束行爲
這裏有個名爲findStringIndex
的非泛型函數,該函數功能是去查找包含一給定String
值的數組。若查找到匹配的字符串,findStringIndex
函數返回該字符串在數組中的索引值(Int
),反之則返回nil
:
func findStringIndex(array: String[], valueToFind: String) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
findStringIndex
函數可以作用於查找一字符串數組中的某個字符串:
let strings = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findStringIndex(strings, "llama") {
println("The index of llama is \(foundIndex)")
}
// 輸出 "The index of llama is 2"
如果只是針對字符串而言查找在數組中的某個值的索引,用處不是很大,不過,你可以寫出相同功能的泛型函數findIndex
,用某個類型T
值替換掉提到的字符串。
這裏展示如何寫一個你或許期望的findStringIndex
的泛型版本findIndex
。請注意這個函數仍然返回Int
,是不是有點迷惑呢,而不是泛型類型?那是因爲函數返回的是一個可選的索引數,而不是從數組中得到的一個可選值。需要提醒的是,這個函數不會編譯,原因在例子後面會說明:
func findIndex<T>(array: T[], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
上面所寫的函數不會編譯。這個問題的位置在等式的檢查上,「if value == valueToFind」
。不是所有的 Swift 中的類型都可以用等式符(==)進行比較。例如,如果你創建一個你自己的類或結構體來表示一個複雜的數據模型,那麼 Swift 沒法猜到對於這個類或結構體而言「等於」的意思。正因如此,這部分代碼不能可能保證工作於每個可能的類型T
,當你試圖編譯這部分代碼時估計會出現相應的錯誤。
不過,所有的這些並不會讓我們無從下手。Swift 標準庫中定義了一個Equatable
協議,該協議要求任何遵循的類型實現等式符(==)和不等符(!=)對任何兩個該類型進行比較。所有的 Swift 標準類型自動支持Equatable
協議。
任何Equatable
類型都可以安全的使用在findIndex
函數中,因爲其保證支持等式操作。爲了說明這個事實,當你定義一個函數時,你可以寫一個Equatable
類型約束作爲類型參數定義的一部分:
func findIndex<T: Equatable>(array: T[], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
findIndex
中這個單個類型參數寫做:T: Equatable
,也就意味着「任何T類型都遵循Equatable
協議」。
findIndex
函數現在則可以成功的編譯過,並且作用於任何遵循Equatable
的類型,如Double
或String
:
let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
// doubleIndex is an optional Int with no value, because 9.3 is not in the array
let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
// stringIndex is an optional Int containing a value of 2