Swift中的泛型

什么时候需要使用泛型

在讲到泛型之前,先写一段代码(文中的代码都是Swift书写)。

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

这是一个很常见的也很简单的Int类型的加法函数。函数 addTwoInt 将传入的两个参数 ab的和返回。
再看一个函数:

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

这个函数是将两个 CGFloat类型的和输出。
如果还需要写Double类型的加法,按照上面的方式再写一个函数就可以了。面对越来越多的类型,需要写的函数也越来越多,幸好,我们能够使用+操作符的类型并不多。尽管如此,这样的方式写代码我们依旧不能忍受。有什么办法让他们统一一下呢?

毕竟,这样一大堆类似的函数除了类型不一样,其余的操作都是完全同质的。

有两种方式可以帮助我们使用一个函数搞定上面说的所有的类型的加法。

  • 使用Swift中的Any类型替代
    我们可以定义以下函数来代替上面的几个类型的加法函数
    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()
    }

这两种方式都可以解决上面说的将多个除类型不同的同质化函数转化成一个。看起来似乎没什么不一样的地方。
其实不是的!看下面的解释

在Swift中,Any类型会避开编译器的类型检测,即使是我们输入了非数字类型调用addTwoNum函数,或者我们返回的不是一个与输入参数同类型的返回值,编译器在编译的时候也不会报错。只有在运行时发现类型不对导致Crash。

而泛型自带了类型推断,也即是在编译过程中,会进行类型推断。addTwo<T>可以理解成一个函数族,编译器会识别其中的类型 T,后续的参数和返回值必须也是类型T,编译才能通过。这样保证了函数使用的确定性。

这样的情况,当然是选择使用泛型嘛!
如果泛型只有这些简单的用处,那确实不怎么地,因为我们依然还时需要在addTwo<T>中判断它的实际类型,不然我们并不能进行相应的操作,这里的操作是 +。如何进行修改呢?

给泛型添加约束

addTwo<T>中,泛型T目前来说只是一个位置的类型。任意类型都可以被做为参数带进来,我们在函数内部仅仅实现了IntCGFloat+操作。如果换成其他的类型,比如 String,那将返回一个致命错误:fatalError()。这种设计显然不合理。使用约束可以规定用户能够使用的类型。

所谓约束,并不是真正的约束,而是对泛型的可选范围进行调整。通常情况下,我们会使用需要泛型遵循必要的协议的方式实现。

一个例子:

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

这是一个比较函数。规定了T必须遵循ComparableComparable是Swift中自带的协议之一。只要遵循这个协议的类别,都可以使用>操作符号进行比较, 返回一个布尔值。Int,String等都遵循了这个协议。
调用:

如果不遵循`Comparable`协议的话,调用报错

如果传入不遵循Comparable协议的类型的时候,比如NSArray,在编译的时候就会报错。上图中。

除此之外,遵循了相应的协议,在comparTwo<T:Comparable>的函数中,不必像addTwo<T>一样需要进行类型判断,代码更加简洁。
Swift中一共有55个协议,并将让它们分成了三大类,有兴趣的可以看看这篇关于关于Swift的55个协议简介的文章。Swift中鼓励我们使用协议,所以很多人说这是面向协议编程,这里不讨论。

返回到addTwo<T>中,我们需要一个可以进行加法的协议。目前我在Swift的协议中并没有找到。倒是在Int和CGFloat中看到它们分别重载了 +,-等等各种操作符。所以,使用加法协议看来是没有现成的了。但是我们可以自己写一套协议NewProtocol,这套协议将定义如何将两个类型进行+操作,然后在每个可能使用的类型中,重载相应的操作符实现。之后再让addTwo<T>中的T遵循这个协议。接下来就跟使用comparTwo<T:Comparable>一样使用addTwo<T:NewProtocol>。这已经 属于如何使用协议的范畴了,在这里也不再仔细讨论。

其实我们已经看出来了,在有已知的可用的协议的情况下,我们可以很方便的使用泛型做到将多个不同但类型同质化函数合并成一个函数。可如果现存的55个协议中,并没有我们想要的协议,那我们就需要自己定义协议,这跟写很多个函数一样,需要写不少东西。有没有什么办法,可以不使用协议而达到同样的目的呢,答案是有的!

泛型和高阶函数结合

上面提到了通过协议可以对一部分指定的类实现泛型编程。但是面对没有现成协议的时候,我们还可以配合高阶函数的使用来解决。
下面我通过另一种方式来实现:

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

调用:

        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
     

是不是简洁了很多倍? 并且这个函数不仅适用于IntCGFloat,还是用于一个String,Double等各种类型。同时,仔细的你肯定发现了,这个函数不仅可以做加法,还可以做减法和其他更多的操作。我们看实际的调用:

      // 针对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

看起来比使用协议的功能还要强大!但是这有个弊端就是:sideFun需要使用者自己去实现,相应的多增加了使用者需要书写的代码,但是相对于这种方式带来的便利,这种弊端可以忽略不计吧。
到此,我们联想到了Swift中数组的一个函数:

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

这是数组的排序函数。跟addTwo类似。但是sorted函数将的实际参数是slef本身(这是数组的扩展)。sorted是Swift的一种泛型类型,跟addTwoT一样。

总结一下,之前说的泛型可以代替一系列操作类似的类型。但是如果泛型和高阶函数一起使用,它则可以代替一系列类似的函数形式。

单纯的泛型,可以替代多种类型,进行同类操作。结合高阶函数,泛型可以将具有相同的函数形式多种操作合为为一,比如addTwo,就是一种使用两个参数,结合一个函数参数,输出不同结果的函数形式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • Swift泛型介绍 泛型是为Swift编程灵活性的一种语法,在函数、枚举、结构体、类中都得到充分的应用,它的引入可...
    Bobby0322阅读 14,133评论 0 26
  • 时隔1个月,2个月,3个月。。。我终于回来了。。公司项目太忙了以至于简书一直没有更新。而且公司项目还是OC的。。最...
    MelodyZhy阅读 1,498评论 1 5
  • 作者:Thomas Hanning,原文链接,原文日期:2015/09/09译者:pmst;校对:numbbbbb...
    梁杰_numbbbbb阅读 428评论 0 4
  • 本文源自于泊学文档,同时在下方添加了扩展的案例,为了方便团队成员翻阅,记录之。 面向对象的方式 在面向对象的世界里...
    AKyS佐毅阅读 810评论 0 3
  • 2月26日下午3点,上嵌在项目六组举办了一场就业面试经验分享会,这次分享邀请到是1611班耿红亮童鞋,他是今年1月...
    嵌入式学习阅读 388评论 0 5