四.类型约束
1. 虽然程序可以对泛型形参传入任何类型,但有时候为泛型形参增加某种特定的约束也是必要的。就像字典,Dictionary的key的类型也是通过泛型形参来表示的,但Dictionary并不允许任意一种类型的数据都能作为key,只有实现了Hashable协议的类型才能作为key。这就是一种类型约束。
2. 类型约束就是指定义类型参数时增加一个限制,约束这种类型参数必须继承指定的类,或遵守一个特定的协议或合成协议。
3. 例如,定义了一个函数实现了对整数进行冒泡排序,当对Double,String类型进行比较是就需要到泛型了,但是又有另一个问题:能排序的数组元素必须是可比较大小的,否则程序无法对数组元素进行排序。也就是说,数组元素必须实现Comparable协议,这就需要使用类型约束了。
4. 定义泛型时使用类型约束的语法格式如下:
<类型形参1:父类,类型形参2:协议,类型形参3: protocol<协议1,协议2>, ...>
上面给出了3种形式的类型约束:
(1)类型形参1:实际传给该形参的类型必须继承指定父类
(2)类型形参2:实际传给该形参的类型必须遵守指定协议
(3)类型形参3:实际传给该形参的类型必须同时遵守合成协议的协议1,协议2
实际中具体需要定义多少个类型形参,完全取决于程序的需要。
5. Swift的String也遵守Comparable协议,因袭String也是可以比较大小的,Swift比较String的大小时按各字符对应的Unicode码进行比较。
五.关联类型
1. 定义协议时,也允许定义一个或多个类型参数,这种类型参数本质上也属于泛型,但需要以关联类型(Associated Type)的形式进行声明。协议中的类型形参需要等到协议被实现时才会确定下来。
2. 在协议中以typealias关键字来声明关联类型。
3. 举个栗子:
protocol Container
{
//声明类型形参,该类型形参需要等到协议被实现时才能确定下来
typealias ItemType
//向容器中添加一个元素
mutating func append(item : ItemType)
//获取容器中元素的个数
var count : Int{}
//根据索引来获取元素
subscript(i : Int) -> ItemType {get}
}
上面程序中,正因为Container协议使用了类型形参,这个类型形参到底代表什么类型,必须等到实现该协议时才能确定下来,这使得该容器可以装任意类型的数据。这就要按实现者要指定什么类型了,例如:
struct IntList : Container
{
//显式指定ItemType类型代表Int类型
typealias ItemType = Int
...
}
4. 我们已经知道,程序可以通过扩展让已有类型来遵守指定协议,包括使用空扩展让已有类型(该类型已经实现了协议的各种要求)遵守已有协议的情形。但没有涉及到泛型,实际上,这种用法完全是支持泛型的。例如:Swift的Array结构体已经提供了append(newEleme: T)方法,count属性,因此程序也可以通过扩展让Array实现Container协议:extension Array: Contaioner{}。这样Array结构体就实现了Container协议,因此Array也可以当成Container使用。
5. 如果程序只需要简单地定义类型形参必须是某个类的子类,或者必须遵守某个协议,使用类型约束即可。如果程序需要对类型形参进行更多复杂的约束,就需要借助于where字句了。
6. where字句也属于泛型声明的一部分,where字句可以指定一个条件表达式,该条件表达式用于对类型形参增加更多额外的限制。在泛型声明中使用where字句的语法如下:
<类型形参:父类型,类型形参2:协议, ... where条件>
7,举个栗子,下面将会定义一个copyContainer函数,这个函数用于将前一个Container中的元素复制到另一个Container中,此时就要求两个Container中的ItemType必须相等,另外,还加了Container容器包含的元素必须是可以打印的要求。
func copyContainer <C1 : Container, C2 : Container
where C1.ItemType == C2.ItemType, C1.ItemType : Printable>(src : C1, inout dest : C2)
{
let count = src.count
for var i=0; i<count; i++
{
dest.append(sec[i])
}
}
上面代码中泛型声明一共有4个要求:
(1)C1类型形参必须遵守Container协议
(2)C2类型形参必须遵守Container协议
(3)C1的ItemType与C2的ItemType必须相同
(4)ItemType必须遵守Printable协议