Swift泛型定义:同时限定T的类(class)和多协议(protocol)

swift 可以定义模板函数,如:

func testFunc<T>(datas: [T]) -> T{
    //处理
}

这里有个T,使用指代类型的,这个方法定义出来,可以用来处理任意的数组:

let names = [String]()
testFunc(names)

let names2 = [Int]()
testFunc(names)

传入String的数组,T就是String;传入Int的数组,T就是Int.这个方法就像是一个模板,有了它,可以复刻出许多个不同的版本。

问题1:我想要这个T是具有某个特定方法的

举个常用的例子:

比如写一个用来找最小值的方法,func min<T>(datas: [T]) -> T?,我肯定是希望它能处理数字、字符串、日期,甚至是自定义类的对象,那就需要这个被排序的数组里的对象,必须得实现一个比较的函数, 这个我才能知道那个大那个小,才能排序。而且我只需要这个比较函数就可以对所有类通用了。

比如可以写成:

func min<T>(datas: [T]) -> T?{
    if datas.count == 0 {
        return nil
    }
    
    var min: T = datas.first!
    for data in datas {
        if data.compareTo(min) < 0{
            min = data
        }
    }
    
    return min
    
}

里面用了compareTo方法。我要怎么确定这个T一定是有这个方法的呢?

祭出法宝:protocol

联想:一些人困惑协议有什么用?跟block/闭包有什么区别?这里的功能就是闭包无法替代协议的,其实协议和委托是可以不一起行动的。

定义一个协议:

protocol Comparable : NSObjectProtocol{
    /**
     和其他对象比较
     - parameter other: 其他对象
     - returns: 0 相等 -1 小于 1 大于
     */
    func compareTo(other: Self) -> Int
}

然后把方法定义修改为:

func min<T:Comparable>(datas: [T]) -> T?

T后面限定了类型,指定了这个T是遵循类Comparable这个协议的,那么也就具有了compareTo这个方法。在我理解里,协议的核心就在这:表明某个类具有特定的方法/能力

问题2:多个协议怎么办?

假如我想处理的T类型是需要具有两个不同的能力,举个现实的例子:一个榨汁机,它接收的东西应该同时具有可被碾碎和出水两个特性,这两个特性是分开的,因为饼干不出水和椰子碾不碎。对应到代码,可悲碾碎是一个protocol里的一个方法,会出水是另一个protocol的方法。

多个协议只需要写成:

func min<T:protocol<testProtocol1,testProtocol2>>(datas: [T]) -> T?

把两个协议用protocol关键字装起来就好了。

问题3:如果我还想这个T是某个特定的类呢?

比如我想T是class1这个类的对象,同时遵循testProtocol1和testProtocol2,怎么写?

祭出法宝:where关键字

方法写成:

func findMinTemplateFunc<T : testCalss
    where T :protocol<testProtocol1,testProtocol2>>(datas: [T]) -> T?

把协议的限定方法where里面去,where还有其他用法,我也没太用过,就不说了。

这个需求看起来好像很难发生,但只需要想一个东西就有了:抽象类。swift/OC里没强掉这个概念,但是这个东西是存在的,比如CoreData里面的NSManageredObject,你会直接使用这个类来构造对象吗?肯定不会,肯定要建自己的数据实体,也就是NSManageredObject的子类来操作了。

当有了抽象类做父类的时候,你处理的都是子类,如果你写一个针对子类的模板方法,有些子类实现了testProtocol1,有些实现了testProtocol2,有些没有。这时,就必须类、协议同时限定才能达到效果。

最后贴段例子:

加入找出数组里最小值,每个值根据value1 \ value2 和rate做一段算法后的值来比较:

protocol testProtocol1: NSObjectProtocol {
    func value1() -> Int;
}

protocol testProtocol2: NSObjectProtocol {
    func value2() -> Int;
}

//类似虚类的东西,比如NSManagedObject这种类,是不可能直接使用它来构建对象的,肯定是要配合自己建的CoreData实体
class testCalss: NSObject {
    var rate: Int? = 1
}

func findMinTemplateFunc<T : testCalss
    where T :protocol<testProtocol1,testProtocol2>>(datas: [T]) -> T?{
    if datas.count == 0 {
        return nil
    }
    var min : T = datas.first!
    var minRealValue = min.value1() * 10 + min.value2() * 100
    if let rate = min.rate {
        minRealValue *= minRealValue * rate
    }
    
    for data in datas {
        var realValue = data.value1() * 10 + data.value2() * 100
        if let rate = data.rate {
            realValue *= realValue * rate
        }
        
        if realValue < minRealValue {
            min = data
            minRealValue = realValue
        }
    }
    
    return min
}

//例子

class subClass1: testCalss, testProtocol1, testProtocol2 {
    
    func value1() -> Int {
        return Int(arc4random() % 10)
    }
    func value2() -> Int {
        return Int(arc4random() % 20)
    }
}

class subClass2: testCalss, testProtocol1, testProtocol2 {
    func value1() -> Int {
        return Int(arc4random() % 100)
    }
    func value2() -> Int {
        return Int(arc4random() % 200)
    }
}

func runTest(){
    var array1 = [subClass1]()
    for _ in 0...99 {
        array1.append(subClass1())
    }
    
    findMinTemplateFunc(array1)
    
    var array2 = [subClass2]()
    for _ in 0...99 {
        array2.append(subClass2())
    }
    
    findMinTemplateFunc(array1)
}

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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,523评论 18 399
  • 132.转换错误成可选值 通过转换错误成一个可选值,你可以使用 try? 来处理错误。当执行try?表达式时,如果...
    无沣阅读 1,230评论 0 3
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,758评论 1 10
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,199评论 25 707
  • 当我第一听优美的钢琴曲(天空之城)演奏者久石让,那时只是光盘。我只记得当时的我眼泪不知不觉就会流出来,听的心...
    呆萌小小阅读 143评论 1 2