泛型(Generics)

泛型的概念:

func swapTwoInts(inout a: Int, inout _ b: Int) {
     let temporaryA = a
     a = b
     b = temporaryA
}//只能交换int类型
 var someInt = 3
 var anotherInt = 107
 swapTwoInts(&someInt, &anotherInt)
 print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
 // Prints "someInt is now 107, and anotherInt is now 3"

这里有一个方法交换int 类型的两个变量,当我们想要交换两个double类型或者string类型的变量又要分别写别的两个函数。这时我们的泛型就展示出来它的优势了,泛型可以用一个方法,代表所有的类型的变量的值交换。但要求这两个变量是相同的类型。

泛型的写法如下:

//在方法名后面加上<T>代表T是类型参数,就像数组里面的Array<Element>
 func swapTwoValues<T>(inout a: T, inout _ b: T) {
      let temporaryA = a
      a = b
      b = temporaryA
 }//可以交换任意类型,但是这两个值的类型必须相同
 //交换int类型
 var someInt = 3
 var anotherInt = 107
 swapTwoValues(&someInt, &anotherInt)
 // someInt is now 107, and anotherInt is now 3
 //交换string类型
 var someString = "hello"
 var anotherString = "world"
 swapTwoValues(&someString, &anotherString)
 // someString is now "world", and anotherString is now "hello"

下面再举一个例子分别定义一个只能储存int类型的IntStack 结构体栈,和可以储存任意类型的Stack结构体栈:

//只能储存int类型
 struct IntStack {
     var items = [Int]()
     mutating func push(item: Int) {
          items.append(item)
     }
     mutating func pop() -> Int {
          return items.removeLast()
     }
 }
 //运用了泛型可以储存任意类型
 struct Stack<Element> {//声明一个<Element>泛型
     var items = [Element]()
     mutating func push(item: Element) {
            items.append(item)
     }
     mutating func pop() -> Element {
            return items.removeLast()
    }
 }
 var stackOfStrings = Stack<String>()//在这里用<String>说明储存的是string类型
 stackOfStrings.push("uno")
 stackOfStrings.push("dos")
 stackOfStrings.push("tres")
 stackOfStrings.push("cuatro")
 // the stack now contains 4 strings
 let fromTheTop = stackOfStrings.pop()
 // fromTheTop is equal to "cuatro", and the stack now contains 3 strings

泛型的扩展:

接着上边的代码可以对Stack 栈进行扩展如:给Stack 扩展一个类型为Element的topItem属性。

extension Stack {
     var topItem: Element? {
            return items.isEmpty ? nil : items[items.count - 1]
     }
 }
 if let topItem = stackOfStrings.topItem {
      print("The top item on the stack is \(topItem).")
 }
 // Prints "The top item on the stack is tres."

类型约束:

并不是所有的类型都能满足函数中的泛型参数,正如字典的键和值(Dictionary<Key, Value> )虽然都是泛型但是字典的键必须满足Hashable 协议。这时候就需要对泛型进行约束了。对泛型的约束有两种。如下面的语法结构:

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
     // function body goes here
 }

约束1:<T: SomeClass>对T的约束是T必须是SomeClass的子类。
约束2:<U: SomeProtocol>对U的约束是U必须实现SomeProtocol这个协议。
类型约束举例:
如下定义一个findIndex函数参数是泛型T,并且通过<T: Equatable>约束T必须实现Equatable这个协议。

func findIndex<T: Equatable>(array: [T], _ valueToFind: T) -> Int? {
     for (index, value) in array.enumerate() {
          if value == valueToFind {
              return index
          }
     }
     return nil
 }
 let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3)
 // doubleIndex is an optional Int with no value, because 9.3 is not in the array
 let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
 // stringIndex is an optional Int containing a value of 2

相关类型:

相关类型跟泛型一样,就是一个暂时不确定的,没有指定的类型,当声明一个协议的时候,有时候需要定义一个或者多个相关类型来帮助协议的声明。相关类型用associatedtype 关键字来标识。

相关类型应用举例:

定义一个Container 协议里面有一个用associatedtype 标识的ItemType,ItemType就是一个相关类型,只有在实现协议的时候才赋值确定ItemType是什么类型。但是能确定的是append方法必须有一个ItemType类型的item参数,subscript方法必须返回一个ItemType 类型的参数。

protocol Container {
     associatedtype ItemType
     mutating func append(item: ItemType)
     var count: Int { get }
     subscript(i: Int) -> ItemType { get }
}
 //在这里IntStack结构体实现了Container 协议
 struct IntStack: Container {
      // original IntStack implementation
      var items = [Int]()
      mutating func push(item: Int) {
          items.append(item)
      }
      mutating func pop() -> Int {
          return items.removeLast()
      }
    // conformance to the Container protocol
    //将ItemType 赋值为具体的Int型,并实现协议的其他方法
     typealias ItemType = Int
     mutating func append(item: Int) {
         self.push(item)
     }
     var count: Int {
        return items.count
     }
     subscript(i: Int) -> Int {
         return items[i]
     }
 }

还可以通过泛型实现Container 协议

struct Stack<Element>: Container {
     // original Stack<Element> implementation
     var items = [Element]()
     mutating func push(item: Element) {
          items.append(item)
     }
     mutating func pop() -> Element {
          return items.removeLast()
    }
    // 这里实现Container 协议的方法
    mutating func append(item: Element) {
          self.push(item)
    }
    var count: Int {
          return items.count
    }
    subscript(i: Int) -> Element {
          return items[i]
    }
 }

扩展已经存在的类型指定一个相关类型:

Array已经存在了append(_:)函数,count属性,以及通过Int的参数元素返回一个subscript所有Array可以实现这个Container 协议并且,写一个空的表达式,写法如下:

extension Array: Container {}

这样你就可以用Container 一样用任何数组。

Where 语句:

接着上面的代码,声明一个allItemsMatch方法通过where语句约束C1要实现Equatable协议,通过自定义的Equatable协议判断实现了Container协议的任意两种类型C1和C2是否相等。判断依据是:
1.someContainer.count != anotherContainer.count返回false。如果someContainer和anotherContainer的元素个数不相等则someContainer和anotherContainer不相等。
2.someContainer[i] != anotherContainer[i]返回false。对任意i如果someContainer[i]和anotherContainer[i]不相等则omeContainer和anotherContainer不相等。
3.如果上面两个条件都满足相等那么返回true则omeContainer和anotherContainer相等。

func allItemsMatch<C1: Container, C2: Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> (someContainer: C1, _ anotherContainer: C2) -> Bool {
      // check that both containers contain the same number of items
     if someContainer.count != anotherContainer.count {
            return false
     }
     // check each pair of items to see if they are equivalent
     for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                   return false
            }
      }
      // all items match, so return true
      return true
 }

即使stackOfStrings属于结构体Stack类型,arrayOfStrings属于数组类型,他们的类型不同,但是他们满足上面的条件即:stackOfStrings元素个数和arrayOfStrings元素个数相同并且对于每一个stackOfStrings[i]都等于arrayOfStrings[i],所以判断结果是stackOfStrings和arrayOfStrings相同将打印All items match.

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

推荐阅读更多精彩内容