Swift學習:泛型

本篇將詳細總結介紹Swift泛型的用法;
Swift泛型代碼讓你能夠根據自定義的需求,編寫出適用于任意類型、靈活可重用的函數及類型。它能讓你避免代碼的重復,用一種清晰和抽象的方式來表達代碼的意圖。

主要內容:
1.泛型解決的問題
2.泛型函數
3.泛型類型
4.擴展一個泛型類型
5.泛型的類型約束
6.關聯類型

一、泛型解決的問題

Swift泛型代碼讓你能夠根據自定義的需求,編寫出適用于任意類型、靈活可重用的函數及類型。它能讓你避免代碼的重復,用一種清晰和抽象的方式來表達代碼的意圖。這種說法很模糊,下面我們結合一個示例來說明泛型的作用。
需求描述:使用函數來交換兩個變量的值

//互換兩個整型
func swapTwoInt(a:inout Int , b:inout Int){
    (a, b) = (b, a)
}

//互換兩個Double
func swapTwoDouble(a:inout Double, b:inout Double){
    (a,b) = (b,a)
}

代碼分析:
swapTwoInt與swapTwoDouble兩個函數功能相同,唯一的區別就是傳入的變量類型不同。這樣的代碼看起來重復又累贅。在實際應用中,通常需要一個更實用更靈活的函數來交換兩個任意類型的值,幸運的是,泛型代碼幫你解決了這種問題。

二、泛型函數

泛型函數可以適用于任何類型,下面的swapTwoValues(::)函數是上面兩個函數的泛型版本,可以交換任意類型的兩個變量。
尖括號里聲明一種通用類型T,參數列表里可以使用這種類型名表示通用類型

func SwapTwoThing<T>(a:inout T, b:inout T){
    (a, b) = (b, a)
}

var a = 100
var b = 200
swapTwoInt(a: &a , b: &b)
a  //200
b  //100

var string1 = "hello"
var string2 = "world"
SwapTwoThing(a: &string1, b: &string2)
string1  //world
string2  //hello

總結泛型函數的使用:
1.使用了占位類型名(T),來替換實際類型名(Int,Double);
2.占位類型符并不指定T必須是什么類型,但是卻限制了參數a和b必須是同一種類型T;
3.只有SwapTwoValues<T>(:)函數在調用時,才能根據所傳入的實際類型決定T所代表的類型;
4.T只是一個符號,可以使用大寫字母開頭的駝峰命名法(例如T和MyTypeParameter)來為類型參數命名,以表明它們是占位類型,而不是一個值。

三、泛型類型

3.1.系統類型使用到的泛型

事實上,泛型類型的使用貫穿了Swift語言。例如,Swift的Array和Dictionary都是泛型集合。你可以創建一個Int數組,也可創建一個String數組。

let arr = Array<Int>()
let dict = Dictionary<String,Int>()
let set  = Set<Float>()

3.2.自定義泛型類型:實現一個棧結構體

除了泛型函數,Swift還允許你定義泛型類型;這些自定義類、結構體和枚舉可以適用于任何類型,類似于Array和 Dictionary。下面的示例就是創建一個具有棧功能的結構體,適用于各種類型。

struct Stack<Element>{
    //存放棧中變量的數組
    var items = Array<Element>()
    
    //入棧:向棧中添加一個新元素
    mutating func push(item:Element){
        items.append(item)
    }
    
    //出棧:刪除棧頂元素,并返回此元素
    mutating func pop() ->Element?{
        return items.removeLast()
    }
}

var stack = Stack<Int>()
stack.push(item: 11)
stack.push(item: 22)
stack.pop()   //22

var stack1 = Stack<String>()
stack1.push(item:"aaa")
stack1.push(item:"bbb")
stack1.pop()  //“bbb"

3.3.自定義泛型類型:多個占位符

自定義泛型類型可以設置多個類型占位符,下面就是自定義了一個泛型類型Pair,它具有兩個占位類型符。

struct Pair<T1, T2>{
    var t1:T1
    var t2:T2
}
var pair1 = Pair(t1: "hello", t2: "hi")
print(pair1)   //Pair<String, String>(t1: "hello", t2: "hi")
var pair2:Pair<String, Int> = Pair(t1:"hello",t2: 123)
print(pair2)   //Pair<String, Int>(t1: "hello", t2: 123)

四、擴展一個泛型類型

擴展一個泛型類型,可以直接使用原始類型定義中聲明的類型參數列表,并且這些來自原始類型中的參數名稱會被用作原始定義中類型參數的引用。
比如,我們現在擴展泛型類型Stack,為其添加計算型屬性topItem,用于獲取棧頂元素,代碼示例如下:

extension Stack {
    //返回當前棧頂元素而不會將其從棧中移除
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}
var stack3 = Stack<Int>()
stack3.push(item:1)
stack3.push(item:2)
stack3.push(item: 3)
if let topItem = stack3.topItem{
    print("棧頂元素:\(topItem)")   //棧頂元素:3
}

注意:擴展中的占位類型符需要與原始類保持一致,所以這里用的還是Element。

五、泛型的類型約束

swapTwoValues(::)函數和Stack類型可以作用于任何類型。但如果可以為泛型函數和泛型類型的類型添加一個特定的類型約束,將會是非常有用的。
通常情況下,我們設置泛型類型約束的時候,會指定一個類型參數必須繼承自指定類,或者符合一個特定的協議或協議組合。

5.1.類型約束語法

對泛型函數添加類型約束的基本語法如下所示(作用于泛型類型時的語法與之相同)。

//在一個類型參數名后面放置一個類名或者協議名,并用冒號進行分隔,來定義類型約束
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 這里是泛型函數的函數體部分
}

5.2.泛型類型約束實踐

下面的泛型函數用于查找數組中某個元素的索引位置;但由于for循環里用到了對象比較"==",要確保所有的類型都適用,所以在泛型函數的中添加了類型約束,使用此泛型函數的參數必須遵循Equatable協議。

func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25])  //nil
let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"]) //2

注意:Swift標準庫定義了Equatable協議,該協議要求任何遵循該協議的類型必須實現等式符(==)及不等符(!=)。從而能對該類型的任意兩個值進行比較。所有的Swift標準類型自動支持 Equatable 協議

六、關聯類型

關聯類型是在為協議中的某個類型提供一個占位名,其所代表的實際類型會在協議被采納時才會被指定。這里涉及到兩個關鍵字,其作用就是給一個類型起一個別名,首先來說明一下:
associatedtype(協議聲明中使用)
typealias (協議實現中使用)
下面通過一個示例來理解關聯類型的作用:定義一個可稱重的協議,其中使用了泛型關聯類型。這種方式可以更大程度的使用協議,具體實現協議的時候再決定類型。

protocol WeightCaclulable{
    //associatedtype設置別名,即關聯類型
    associatedtype WeightType
    var weight:WeightType{get} //返回重量屬性,其類型是WeightType
}

//iphone7:手機較輕,表示重量時會有小數點,所以使用Double描述
class Iphone7:WeightCaclulable{
    //實現的時候用的是typealias
    typealias WeightType  = Double
    
    var weight: Double {
        return 0.114
    }
}
//Ship:輪船較重,表示重量可以忽略小數,所以使用Int描述
class Ship:WeightCaclulable{
    typealias WeightType = Int
    var weight: WeightType
    
    init(weight:WeightType) {
        self.weight = weight
    }
}
let iphone7 = Iphone7()
print(iphone7.weight)  //0.114
let ship = Ship(weight: 100000)
print(ship.weight)     //100000

6.1.關聯類型添加約束

協議中存在關聯類型,我們也可以為其添加約束,下面是一個Container協議,我們設置其關聯類型Item遵循了協議Equatable,具體代碼如下:

protocol Container {
    associatedtype Item: Equatable
    
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,296評論 2 376

推薦閱讀更多精彩內容

  • 泛型代碼可以確保你寫出靈活的,可重用的函數和定義出任何你所確定好的需求的類型。你的可以寫出避免重復的代碼,并且用一...
    iOS_Developer閱讀 809評論 0 0
  • 本章將會介紹 泛型所解決的問題泛型函數類型參數命名類型參數泛型類型擴展一個泛型類型類型約束關聯類型泛型 Where...
    寒橋閱讀 647評論 0 2
  • 136.泛型 泛型代碼讓你可以寫出靈活,可重用的函數和類型,它們可以使用任何類型,受你定義的需求的約束。你可以寫出...
    無灃閱讀 1,492評論 0 4
  • 泛型(Generics) 泛型代碼允許你定義適用于任何類型的,符合你設置的要求的,靈活且可重用的 函數和類型。泛型...
    果啤閱讀 687評論 0 0
  • 悠蘭噠閱讀 297評論 0 0