Swift之Generics

泛型发展

  1. 泛型程序最早出现在1970年的CLU和Ada语言中,后来被 许多基于对象和面向对象的语言采用,包括C++、Java、VB等。
  2. 1971年,Dave Musser首先提出闭关推广了泛型编程理论,但是局限于软件开发和计算机代数领域。1979年,Alexander Stepanov开始研究泛型编程,并提出STL体系结构。
  3. 1993年,Stepanov受邀在ANSI/ISO C++标准委员会的会议上介绍了泛型编程理论及其相关工作。1994年3月,Stepanov和Meng Lee提出STL草案,1994年7月ANSI/ISO C++标准委员会通过了修改后的STL草案。
  4. C++泛型典型应用:STL和Boost.

定义泛型函数

 func sawpTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a =  b
    b = temp
 }

定义泛型类型

 struct Stack<Element> {
    var items = [Element]()
    mutating func push(element: Element){
        items.append(element)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
 }

扩展时不需要提供形式参数列表:

extension Stack {
    var top : Element? {
        return items.last
    }
}

泛型类型约束

为什么需要类型约束

上述函数和stack可以适用于任意类型,但是于是需要添加强制的类型约束。类型约束指的是一个类型形式参数必须继承自特定类,或者遵循特定协议、组合协议。比如swift中的字典,声明如下:

struct Dictionary<Key, Value> where Key : Hashable

就在键的类型上添加了约束,必须可以哈希,即必须提供一种使其可以唯一表示的方法。如果键值不可哈希,我们在插入删除以及更新值的时候回发生混乱。
类型约束语法:

func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        //因为用到==,所以类型必须遵循Equatable协议。
        if value  == valueToFind {
            return index
        }
    }
    return nil
}

如果没有添加遵循Equatable协议的要求,则会有如下报错,告诉你可以使用==的所有类型。

SwiftPractice.playground:24:20: note: overloads for '==' exist with these partially matching parameter lists: ((), ()), ((A, B), (A, B)), ((A, B, C), (A, B, C)), ((A, B, C, D), (A, B, C, D)), ((A, B, C, D, E), (A, B, C, D, E)), ((A, B, C, D, E, F), (A, B, C, D, E, F)), (Any.Type?, Any.Type?), (AnyHashable, AnyHashable), (AnyIndex, AnyIndex), (AnyKeyPath, AnyKeyPath), (Array<Element>, Array<Element>), (ArraySlice<Element>, ArraySlice<Element>), (Bool, Bool), (CGAffineTransform, CGAffineTransform), (CGPoint, CGPoint), (CGRect, CGRect), (CGSize, CGSize), (CGVector, CGVector), (Calendar, Calendar), (Calendar.Component, Calendar.Component), (Calendar.Identifier, Calendar.Identifier), (Calendar.MatchingPolicy, Calendar.MatchingPolicy), (Calendar.RepeatedTimePolicy, Calendar.RepeatedTimePolicy), (Calendar.SearchDirection, Calendar.SearchDirection), (Character, Character), (CharacterSet, CharacterSet), (ClosedRange<Bound>, ClosedRange<Bound>), (ClosedRange<Bound>.Index, ClosedRange<Bound>.Index), (CodingUserInfoKey, CodingUserInfoKey), (CollectionDifference<ChangeElement>, CollectionDifference<ChangeElement>), (CollectionDifference<ChangeElement>.Change, CollectionDifference<ChangeElement>.Change), (CollectionDifference<ChangeElement>.Index, CollectionDifference<ChangeElement>.Index), (ContiguousArray<Element>, ContiguousArray<Element>), (DarwinBoolean, DarwinBoolean), (Data, Data), (Date, Date), (DateComponents, DateComponents), (DateInterval, DateInterval), (Decimal, Decimal), (Dictionary<Key, Value>.Index, Dictionary<Key, Value>.Index), (Dictionary<Key, Value>.Keys, Dictionary<Key, Value>.Keys), (DispatchQoS, DispatchQoS), (DispatchQoS.QoSClass, DispatchQoS.QoSClass), (DispatchQueue.AutoreleaseFrequency, DispatchQueue.AutoreleaseFrequency), (DispatchQueue.GlobalQueuePriority, DispatchQueue.GlobalQueuePriority), (DispatchQueue.SchedulerTimeType.Stride, DispatchQueue.SchedulerTimeType.Stride), (DispatchTime, DispatchTime), (DispatchTimeInterval, DispatchTimeInterval), (DispatchTimeoutResult, DispatchTimeoutResult), (DispatchWallTime, DispatchWallTime), (EmptyCollection<Element>, EmptyCollection<Element>), (FlattenSequence<Base>.Index, FlattenSequence<Base>.Index), (FloatingPointClassification, FloatingPointClassification), (FloatingPointRoundingRule, FloatingPointRoundingRule), (FloatingPointSign, FloatingPointSign), (IndexPath, IndexPath), (IndexSet, IndexSet), (IndexSet.Index, IndexSet.Index), (IndexSet.RangeView, IndexSet.RangeView), (Int, Int), (Int16, Int16), (Int32, Int32), (Int64, Int64), (Int8, Int8), (LazyPrefixWhileSequence<Base>.Index, LazyPrefixWhileSequence<Base>.Index), (Locale, Locale), (ManagedBufferPointer<Header, Element>, ManagedBufferPointer<Header, Element>), (Measurement<LeftHandSideType>, Measurement<RightHandSideType>), (Mirror.DisplayStyle, Mirror.DisplayStyle), (NSDirectionalEdgeInsets, NSDirectionalEdgeInsets), (NSObject, NSObject), (NSObject.KeyValueObservingPublisher<Subject, Value>, NSObject.KeyValueObservingPublisher<Subject, Value>), (NSRange, NSRange), (Never, Never), (Notification, Notification), (NotificationCenter.Publisher, NotificationCenter.Publisher), (ObjectIdentifier, ObjectIdentifier), (OpaquePointer, OpaquePointer), (OperationQueue.SchedulerTimeType.Stride, OperationQueue.SchedulerTimeType.Stride), (PersonNameComponents, PersonNameComponents), (Range<Bound>, Range<Bound>), (Result<Success, Failure>, Result<Success, Failure>), (ReversedCollection<Base>.Index, ReversedCollection<Base>.Index), (RunLoop.SchedulerTimeType.Stride, RunLoop.SchedulerTimeType.Stride), (Scanner.NumberRepresentation, Scanner.NumberRepresentation), (Selector, Selector), (Self, Other), (Self, RHS), (Set<Element>, Set<Element>), (Set<Element>.Index, Set<Element>.Index), (String, String), (String.Encoding, String.Encoding), (String.Index, String.Index), (TimeZone, TimeZone), (UIEdgeInsets, UIEdgeInsets), (UIFloatRange, UIFloatRange), (UIOffset, UIOffset), (UInt, UInt), (UInt16, UInt16), (UInt32, UInt32), (UInt64, UInt64), (UInt8, UInt8), (URL, URL), (URLComponents, URLComponents), (URLQueryItem, URLQueryItem), (URLRequest, URLRequest), (UUID, UUID), (Unicode.CanonicalCombiningClass, Unicode.CanonicalCombiningClass), (Unicode.GeneralCategory, Unicode.GeneralCategory), (Unicode.NumericType, Unicode.NumericType), (Unicode.Scalar, Unicode.Scalar), (Unicode.UTF32, Unicode.UTF32), (UnicodeDecodingResult, UnicodeDecodingResult), (Wrapped?, Wrapped?), (Wrapped?, _OptionalNilComparisonType), ([Key : Value], [Key : Value]), (_OptionalNilComparisonType, Wrapped?), (_UIntBuffer<Element>.Index, _UIntBuffer<Element>.Index), (_ValidUTF8Buffer.Index, _ValidUTF8Buffer.Index)
         if value  == valueToFind {

泛型协议

Protocols do not allow generic parameters; use associated types instead

泛型协议通过关联类型实现,管理按类型是协议中类型的占位符。

关联类型

 protocol Container {
     associatedtype ItemType
     mutating func append(_ item: ItemType)
     var count: Int { get }
     subscript(i: Int) -> ItemType { get }
 }

具体实例化的例子:

 struct IntStack: Container {
    var items = [Int]()
    mutating func push(element: Int){
        items.append(element)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }

    //必须指明关联类型
    typealias ItemType = Int
    mutating func append(_ item: Int) {
        self.push(element: item)
    }

    var count: Int {
        return items.count
    }

    subscript(i: Int) -> Int {
        return items[i]
    }
 }

因为swift强大的类型推断功能,反省类型遵循协议时,可以省略协议中关联类型的显示指定。

 struct Stack<Element>: Container {
    var items = [Element]()
    mutating func push(element: Element){
        items.append(element)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
    mutating func append(_ item: Element) {
        self.push(element: item)
    }

    var count: Int {
        return items.count
    }

    subscript(i: Int) -> Element {
        return items[i]
    }
 }

关联类型的约束

关联类型的类型约束添加方法与泛型类型中类型约束方法一样,冒号后面添加继承的类或者遵循的协议

 protocol Container {
    //关联类型约束
    associatedtype ItemType: Equatable
    mutating func append(_ item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
 }

协议本身可以作为其关联类型的类型约束。

protocol Container {
    //关联类型约束
    associatedtype ItemType: Equatable
    mutating func append(_ item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
 }

 protocol SuffixableContainer: Container {
    associatedtype Suffix: SuffixableContainer where Suffix.ItemType == ItemType
    func suffix(_ size: Int) -> Suffix
 }

 struct Stack<Element: Equatable>: Container {
    var items = [Element]()
    mutating func push(element: Element){
        items.append(element)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
    mutating func append(_ item: Element) {
        self.push(element: item)
    }

    var count: Int {
        return items.count
    }

    subscript(i: Int) -> Element {
        return items[i]
    }
 }

 extension Stack: SuffixableContainer {
    func suffix(_ size: Int) -> Stack {
        var ans = Stack()
        for index in (count  - size)..<count {
            ans.append(self[index])
        }
        return ans
    }
 }

where子句

where子句应用于函数。

 protocol Container {
    associatedtype ItemType
    mutating func append(_ item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
 }

 func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.ItemType == C2.ItemType, C1.ItemType: Equatable
 {
    if someContainer.count != anotherContainer.count {
        return false
    }
    for i in 0..<someContainer.count {
        if someContainer[i] != anotherContainer[i] {
            return false
        }
    }
    return true
 }

where子句也可以应用在扩展之中。

 extension Array where Element: BinaryInteger  {
    var average: Double {
        return self.reduce(0.0) {$0 +  Double($1)} / Double(count)
    }
 }
 extension Array where Element == Double {
    var average: Element {
        return self.reduce(0.0) {$0 +  $1} / Double(count)
    }
 }

where子句应用于关联类型。

 protocol Container {
    associatedtype ItemType
    mutating func append(_ item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
    associatedtype Iterator: IteratorProtocol where Iterator.Element == ItemType
    func makeIterator() -> Iterator
 }

泛型下标

 protocol Container {
    associatedtype ItemType
    mutating func append(_ item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
 }

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

推荐阅读更多精彩内容

  • 本章将会介绍 泛型所解决的问题泛型函数类型参数命名类型参数泛型类型扩展一个泛型类型类型约束关联类型泛型 Where...
    寒桥阅读 633评论 0 2
  • 泛型代码使您能够编写灵活的、可重用的函数和类型,这些函数和类型可以使用任何类型,取决于您定义的需求。您可以编写避免...
    微笑中的你阅读 427评论 0 0
  • 泛型的概念 泛型代码可根据自定义需求,写出适用于任何类型、灵活且可重用的函数和类型,避免重复的代码,用一种清晰和抽...
    伯wen阅读 398评论 0 2
  • 泛型代码 能够根据自定义的需求,编写出适用于任意类型、灵活可重用的函数及类型。避免代码的重复,用一种清晰和抽象的方...
    答案MK阅读 411评论 0 0
  • 1 夕阳西下,太阳发出一道道霞光,洒在美丽的筼筜湖畔,将波光粼粼的湖面照得通红。湖畔边,是一条弯弯曲曲的小径,像一...
    江离菲菲阅读 1,480评论 4 11