Swift中的泛型

什么時(shí)候需要使用泛型

在講到泛型之前,先寫(xiě)一段代碼(文中的代碼都是Swift書(shū)寫(xiě))。

func addTwoInt(_ a:Int,_ b:Int)->Int{
   return a+b
}

這是一個(gè)很常見(jiàn)的也很簡(jiǎn)單的Int類型的加法函數(shù)。函數(shù) addTwoInt 將傳入的兩個(gè)參數(shù) ab的和返回。
再看一個(gè)函數(shù):

func addTwoFloat(_ a:CGFloat,_ b:CGFloat)->CGFloat{
      return a+b
}

這個(gè)函數(shù)是將兩個(gè) CGFloat類型的和輸出。
如果還需要寫(xiě)Double類型的加法,按照上面的方式再寫(xiě)一個(gè)函數(shù)就可以了。面對(duì)越來(lái)越多的類型,需要寫(xiě)的函數(shù)也越來(lái)越多,幸好,我們能夠使用+操作符的類型并不多。盡管如此,這樣的方式寫(xiě)代碼我們依舊不能忍受。有什么辦法讓他們統(tǒng)一一下呢?

畢竟,這樣一大堆類似的函數(shù)除了類型不一樣,其余的操作都是完全同質(zhì)的。

有兩種方式可以幫助我們使用一個(gè)函數(shù)搞定上面說(shuō)的所有的類型的加法。

  • 使用Swift中的Any類型替代
    我們可以定義以下函數(shù)來(lái)代替上面的幾個(gè)類型的加法函數(shù)
    func addTwoNum(_ a:Any,_ b:Any)->Any{
        if let intA = a as? Int,let intB = b as? Int
        {
            return intA + intB
        }
        if let floatA = a as? CGFloat,let floatB = b as? CGFloat
        {
            return floatA + floatB
        }
        fatalError()
    }
  • 使用泛型
    下面的代碼使用泛型替代
    func addTwo<T>(_ a:T,_ b:T)->T{
        if let intA = a as? Int,let intB = b as? Int
        {
            return intA + intB as! T
        }
        if let floatA = a as? CGFloat,let floatB = b as? CGFloat
        {
            return floatA + floatB as! T
        }
        fatalError()
    }

這兩種方式都可以解決上面說(shuō)的將多個(gè)除類型不同的同質(zhì)化函數(shù)轉(zhuǎn)化成一個(gè)。看起來(lái)似乎沒(méi)什么不一樣的地方。
其實(shí)不是的!看下面的解釋

在Swift中,Any類型會(huì)避開(kāi)編譯器的類型檢測(cè),即使是我們輸入了非數(shù)字類型調(diào)用addTwoNum函數(shù),或者我們返回的不是一個(gè)與輸入?yún)?shù)同類型的返回值,編譯器在編譯的時(shí)候也不會(huì)報(bào)錯(cuò)。只有在運(yùn)行時(shí)發(fā)現(xiàn)類型不對(duì)導(dǎo)致Crash。

而泛型自帶了類型推斷,也即是在編譯過(guò)程中,會(huì)進(jìn)行類型推斷。addTwo<T>可以理解成一個(gè)函數(shù)族,編譯器會(huì)識(shí)別其中的類型 T,后續(xù)的參數(shù)和返回值必須也是類型T,編譯才能通過(guò)。這樣保證了函數(shù)使用的確定性。

這樣的情況,當(dāng)然是選擇使用泛型嘛!
如果泛型只有這些簡(jiǎn)單的用處,那確實(shí)不怎么地,因?yàn)槲覀円廊贿€時(shí)需要在addTwo<T>中判斷它的實(shí)際類型,不然我們并不能進(jìn)行相應(yīng)的操作,這里的操作是 +。如何進(jìn)行修改呢?

給泛型添加約束

addTwo<T>中,泛型T目前來(lái)說(shuō)只是一個(gè)位置的類型。任意類型都可以被做為參數(shù)帶進(jìn)來(lái),我們?cè)诤瘮?shù)內(nèi)部?jī)H僅實(shí)現(xiàn)了IntCGFloat+操作。如果換成其他的類型,比如 String,那將返回一個(gè)致命錯(cuò)誤:fatalError()。這種設(shè)計(jì)顯然不合理。使用約束可以規(guī)定用戶能夠使用的類型。

所謂約束,并不是真正的約束,而是對(duì)泛型的可選范圍進(jìn)行調(diào)整。通常情況下,我們會(huì)使用需要泛型遵循必要的協(xié)議的方式實(shí)現(xiàn)。

一個(gè)例子:

    func comparTwo<T:Comparable>( _ a:T,_ b:T) -> Bool {
        return a>b
    }

這是一個(gè)比較函數(shù)。規(guī)定了T必須遵循Comparable,Comparable是Swift中自帶的協(xié)議之一。只要遵循這個(gè)協(xié)議的類別,都可以使用>操作符號(hào)進(jìn)行比較, 返回一個(gè)布爾值。Int,String等都遵循了這個(gè)協(xié)議。
調(diào)用:

如果不遵循`Comparable`協(xié)議的話,調(diào)用報(bào)錯(cuò)

如果傳入不遵循Comparable協(xié)議的類型的時(shí)候,比如NSArray,在編譯的時(shí)候就會(huì)報(bào)錯(cuò)。上圖中。

除此之外,遵循了相應(yīng)的協(xié)議,在comparTwo<T:Comparable>的函數(shù)中,不必像addTwo<T>一樣需要進(jìn)行類型判斷,代碼更加簡(jiǎn)潔。
Swift中一共有55個(gè)協(xié)議,并將讓它們分成了三大類,有興趣的可以看看這篇關(guān)于關(guān)于Swift的55個(gè)協(xié)議簡(jiǎn)介的文章。Swift中鼓勵(lì)我們使用協(xié)議,所以很多人說(shuō)這是面向協(xié)議編程,這里不討論。

返回到addTwo<T>中,我們需要一個(gè)可以進(jìn)行加法的協(xié)議。目前我在Swift的協(xié)議中并沒(méi)有找到。倒是在Int和CGFloat中看到它們分別重載了 +,-等等各種操作符。所以,使用加法協(xié)議看來(lái)是沒(méi)有現(xiàn)成的了。但是我們可以自己寫(xiě)一套協(xié)議NewProtocol,這套協(xié)議將定義如何將兩個(gè)類型進(jìn)行+操作,然后在每個(gè)可能使用的類型中,重載相應(yīng)的操作符實(shí)現(xiàn)。之后再讓addTwo<T>中的T遵循這個(gè)協(xié)議。接下來(lái)就跟使用comparTwo<T:Comparable>一樣使用addTwo<T:NewProtocol>。這已經(jīng) 屬于如何使用協(xié)議的范疇了,在這里也不再仔細(xì)討論。

其實(shí)我們已經(jīng)看出來(lái)了,在有已知的可用的協(xié)議的情況下,我們可以很方便的使用泛型做到將多個(gè)不同但類型同質(zhì)化函數(shù)合并成一個(gè)函數(shù)??扇绻F(xiàn)存的55個(gè)協(xié)議中,并沒(méi)有我們想要的協(xié)議,那我們就需要自己定義協(xié)議,這跟寫(xiě)很多個(gè)函數(shù)一樣,需要寫(xiě)不少東西。有沒(méi)有什么辦法,可以不使用協(xié)議而達(dá)到同樣的目的呢,答案是有的!

泛型和高階函數(shù)結(jié)合

上面提到了通過(guò)協(xié)議可以對(duì)一部分指定的類實(shí)現(xiàn)泛型編程。但是面對(duì)沒(méi)有現(xiàn)成協(xié)議的時(shí)候,我們還可以配合高階函數(shù)的使用來(lái)解決。
下面我通過(guò)另一種方式來(lái)實(shí)現(xiàn):

    func addTwo<T>( _ a:T,_ b:T,_ sideFun:(T,T)->(T)) -> T {
        return sideFun(a,b)
    }

調(diào)用:

        let intA:Int = 5
        let intB:Int = 15
        let floatC:CGFloat = 0.6
        let floatD:CGFloat = 1.62
        
        print(self.addTwo(intA, intB) { (a, b) -> Int in
            return a+b
        })     //  20
        
        print(self.addTwo(floatC, floatD) { (a, b) -> CGFloat in
            return a+b
        })    // 2.22
     

是不是簡(jiǎn)潔了很多倍? 并且這個(gè)函數(shù)不僅適用于IntCGFloat,還是用于一個(gè)String,Double等各種類型。同時(shí),仔細(xì)的你肯定發(fā)現(xiàn)了,這個(gè)函數(shù)不僅可以做加法,還可以做減法和其他更多的操作。我們看實(shí)際的調(diào)用:

      // 針對(duì)String類型
        let newString = self.addTwo("第一段字符", "第二段字符") { (a, b) -> String in
            return a+b
        }
        print(newString)   //   第一段字符第二段字符
        // 減法
        print(self.addTwo(intA, intB) { (a, b) -> Int in
            return a-b
        })  //  -10
        
        print(self.addTwo(floatC, floatD) { (a, b) -> CGFloat in
            return a-b
        })  // -1.02

看起來(lái)比使用協(xié)議的功能還要強(qiáng)大!但是這有個(gè)弊端就是:sideFun需要使用者自己去實(shí)現(xiàn),相應(yīng)的多增加了使用者需要書(shū)寫(xiě)的代碼,但是相對(duì)于這種方式帶來(lái)的便利,這種弊端可以忽略不計(jì)吧。
到此,我們聯(lián)想到了Swift中數(shù)組的一個(gè)函數(shù):

    public func sorted(by areInIncreasingOrder: (Element, Element) -> Bool) -> [Element]

這是數(shù)組的排序函數(shù)。跟addTwo類似。但是sorted函數(shù)將的實(shí)際參數(shù)是slef本身(這是數(shù)組的擴(kuò)展)。sorted是Swift的一種泛型類型,跟addTwoT一樣。

總結(jié)一下,之前說(shuō)的泛型可以代替一系列操作類似的類型。但是如果泛型和高階函數(shù)一起使用,它則可以代替一系列類似的函數(shù)形式。

單純的泛型,可以替代多種類型,進(jìn)行同類操作。結(jié)合高階函數(shù),泛型可以將具有相同的函數(shù)形式多種操作合為為一,比如addTwo,就是一種使用兩個(gè)參數(shù),結(jié)合一個(gè)函數(shù)參數(shù),輸出不同結(jié)果的函數(shù)形式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift泛型介紹 泛型是為Swift編程靈活性的一種語(yǔ)法,在函數(shù)、枚舉、結(jié)構(gòu)體、類中都得到充分的應(yīng)用,它的引入可...
    Bobby0322閱讀 14,268評(píng)論 0 26
  • 時(shí)隔1個(gè)月,2個(gè)月,3個(gè)月。。。我終于回來(lái)了。。公司項(xiàng)目太忙了以至于簡(jiǎn)書(shū)一直沒(méi)有更新。而且公司項(xiàng)目還是OC的。。最...
    MelodyZhy閱讀 1,568評(píng)論 1 5
  • 作者:Thomas Hanning,原文鏈接,原文日期:2015/09/09譯者:pmst;校對(duì):numbbbbb...
    梁杰_numbbbbb閱讀 469評(píng)論 0 4
  • 本文源自于泊學(xué)文檔,同時(shí)在下方添加了擴(kuò)展的案例,為了方便團(tuán)隊(duì)成員翻閱,記錄之。 面向?qū)ο蟮姆绞?在面向?qū)ο蟮氖澜缋?..
    AKyS佐毅閱讀 904評(píng)論 0 3
  • 2月26日下午3點(diǎn),上嵌在項(xiàng)目六組舉辦了一場(chǎng)就業(yè)面試經(jīng)驗(yàn)分享會(huì),這次分享邀請(qǐng)到是1611班耿紅亮童鞋,他是今年1月...
    嵌入式學(xué)習(xí)閱讀 472評(píng)論 0 5

友情鏈接更多精彩內(nèi)容