Generics(泛函数,泛类型)

2016-08-07  本文已影响58人  查无此人123
写在前面

Generics的功效

Generics是为了解决简化那些功能完全一样,但参数类型不一样的方法而存在的,可以免去写很多重复的方法,比如:

func swapTwoInts(inout a: Int, inout _ b: Int) {
  //括号里是代码实现,我省去了
}

func swapTwoStrings(inout a: Stirng, inout _ b: String) {
  //括号里是代码实现,我省去了,与上面代码实现完全一样
}

可以看到上面这两种方法,都是为了交换前后两个参数而存在的,它们也就只有传入的两个参数的类型不同,函数名不同而已,其它都是相同的,所以,就有了Generics的出现.如下:

func swapTwoValues<T>(inout a : T, inout _ b: T) {
  //此处为代码实现,省略
}

Generics Method(泛函数,泛方法)

这就是Generics方法(泛函数,泛方法),你能很明显发现:

Type Parameters(类型参数)

Generic Type(泛类型)

Generic除了可以改进方法,还可以改进Type(类)

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

var stackOfStrings = Stack<String>()

这样就可以让一个stack<Element>类放入各种类型了.

Extending a Generic Type(对泛类型进行扩展)

既然右泛类型,那么就可以给泛类型进行扩展,扩展关键词是extension,这种扩展就不需要像泛函数那样需要类型参数列表,只需要extension+类型名就好了,然后在这个扩展里面写新的方法新的变量的时候,依然可以引用原类型里的泛类型参数,如下:(承接上面的例子)

extension Stack {
  var topItem: Element? {
    return items.isEmpty ? nil : items[items.count - 1]
  }
}

这段代码是对上一段代码定义的泛类型(泛结构体)Stack的一个扩展,可以看到以下几点:

Type Constraints(泛类型的类型约束)

既然有了泛类型,就会有泛类型的类型约束,来对我们创造出的泛类型进行类型约束,而这种类型约束就两种:

下面是代码示例:

func someFucntion<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
  //代码实现就省略了哈~
}

可以看到上面的示例是描述了一个名为someFucntion的函数,函数名后面跟了

<T: SomeClass, U: SomeProtocol,意思是,告诉swift这个方法定义了两个placeholder(占位符),它们分别是T与U,这两个参数稍后会在函数声明的后半段,也就是紧接着的小括号里会作为两个参数传入函数中,所以结合上面学习的类型参数,这两个占位符T与U就是有了新的更准确的名字叫做Type Parameter(类型参数),或者可以叫它泛类型参数(好像没有看到这么叫的,我自己这么叫方便我自己理解),同时这两个泛类型参数还分别要遵循各自的约束(Type Constraints),类型参数T需要继承自某个类(SomeClass),类型参数U则要遵循某个协议(SomeProtocol)

另,文档中提到了两种协议一个HashableEquatable,Hashable是为了让让每个字符,元素神马神马的都拥有独一无二的编码,不会重复,且能准确提取查找,String,Int,Double,Bool都是默认遵循Hashable的;而Equatable则是要保证元素可以做==!=的操作.

Associated Types(这我也不知道怎么翻....)

这个Associated Types 我也不晓得怎么翻译,就直接打原名吧,它貌似是为了protocol而存在的,在Protocol里引入一个placeholder(占位符),然后直到该协议(protocol)被真正引用并用代码实现调用的时候,才会知道它真正代表是个神马类型,见下面代码:

protocol Container {

  associatedtype ItemType //这句是重点!
  mutating func append(item: ItemType)
  var count: Int { get }
  subscript(i: Int) -> ItemType { get }
  
}

可以看到Associated Type的关键词(key)是associatedtype,也就是说这个关键词的后面定义的就是所谓的Associated Type,也就是协议(protocol)里的占位符(placeholder,我还是喜欢叫它泛占位符,方便记忆,哈哈)

还记得上面学到的extension吗,在扩展一个泛类型(generic type)的时候,虽然不用在类名(如:Stack)后写<T,U,Element>这样的类型参数类表,但在扩展的同时,如果又希望它能遵循一个新的protocol的话,就需要在类名后面写出来了,代码如下:

extension Array: Container {
  //代码实现省略
}

从代码中可以看到这次扩展是让Array这个类在扩展的同时,还遵循了Container这个协议(protocol),注意尊许protocol是不需要写尖括号<>的,直接在类名后加冒号:+协议名

Where Clauses(where从句)

这个标题起始并不好,应该写成Associated Type Constraints,为什么呢?因为类型参数(generic parameter)可以加约束(Type Constraints),同样类比过来,协议(protocol)也可以加约束,而这个约束的实现方法就是使用where从句(Where Clauses).

下面看代码,印证以下:

func allItemsMatch< C1: Container, C2:Container where C1.ItemType == C2.ItemType, C1.ItemType: Equatable >(someContainer: C1, _ anotherContainer: C2) -> Bool {
  //代码实现就省略了哈~
}

可以看到,泛函数allItemsMatch有两个泛类型参数C1与C2,它们都加了约束,约束为遵循Cantainer协议,而后面接了一个where关键词,从这里开始就是给这个Container协议加额外的约束,约束有两个,一个是说C1与C2在遵循Containner协议后(Containner协议中有一个Associated Type,在之前的代码中可以看到,也就是ItemType),C1与C2都可以取道自己的Associated Type,也就是C1.ItemTypeC2.ItemType,第一个约束也就是在要求这两个参数要相等;而第二约束是给这两个参数加了另外一个协议(Equatable),意思是在这两个参数比较是否相等之前,还要确定其遵循Equatable协议,才能比较,换句话说,你只有可以使用==!=你才能比较是不是相等,如果连这两个相等与不能的操作符都不能用,那也比较不了.

总结

好了泛类型看完了,总结一下

一句话总结: Generic实际上是在讲一个叫做Generic的占位符(placeholder)在函数和类中的定义和应用.

详细总结:

​ 这个占位符作为参数传给函数或者应用在类中定义变量的时候就叫做Type Parameter(类型参数)

​ 它如果出现在protocol中就叫做Associated Type

​ 这两种占位符我们统称为Generic

​ 而作为Type Parameter(类型参数)的时候我们就可以给它加Constraints(约束)

Constraints(约束)又分为Inherit(继承)Protocol(协议)

​ 其中我们还可以给Protocol(协议)Constraints(约束),而这个Constraint(约束)where从句

Generic Tree(泛类型知识树)

上一篇 下一篇

猜你喜欢

热点阅读