泛型可以使代码更加简洁,避免代码重复,类型检查更加严格。泛型是根据需求定义的,适用于任何类型的,灵活且可重用的函数和类型。避免重复的代码,以一种清晰和抽象的方法来表达代码的意图。占位符的位置在函数名、结构体名等名称的后面,紧随名称。例如:swapTwoValues<T>。
泛型函数
可以使用占位类型名字(通常使用字母T来表示)来替代实际类型名(Int等)。虽然没有强调参数类型是哪一种,但是参数的类型必定是相同的。
func swapTwoValues<T>(_ left:inout T, _
right:inout T){
let temp = left
left =
right
right = temp
}
var
one =
20
var
two =
30
swapTwoValues(&
one, &
two)
print(
one)
也支持多个类型参数,命名在尖括号内,用逗号隔开。
func swapTwoValues<T,S>(_ left:inout T, _
right:inout T,center:S){
print(
"\(center)")
let temp = left
left =
right
right = temp
}
var
one =
20
var
two =
30
swapTwoValues(&
one, &
two, center:
"test")
print(
one)
如果使用多个类型参数,那么就需要更多的描述类型参数。例如字典的键值。在命名时,使用大写字母开头的驼峰命名法来类型参数命名,例如:KeyType,以表明这些是类型的占位符,而不是类型值。
泛型类型
泛型类型的结构体:
struct Stack<T>{
var
items = [T]()
mutating func push(
item:T){
items.append(
item)
}
mutating func pop() -> T{
return items.removeLast()
}
}
var oneStack = Stack<String>()
oneStack.push(
item:
"first")
oneStack.push(
item:
"second")
oneStack.push(
item:
"Third")
oneStack.push(
item:
"Four")
在声明时,需要确认类型参数的类型。
扩展泛型类型
如果扩展一个泛型,不需要额外提供类型参数。因为可以从原来的类或者结构体重使用原来定义的泛型对象T。代码示例:
extension Stack {
var topItem:T?{
return items.isEmpty ? nil :
items[
items.count -
1]
}
}
print(oneStack.topItem!)
类型约束
类似于字典的键值必须是可哈希化的,这就是一种类型约束。当创建自定义泛型类型时,可以定义你自己的类型约束。这些约束要支持泛型编程的强力特征中的多数。抽象概念如可哈希化具有的类型特征是根据它们的概念特征来界定的,而不是它们的直接类型特征。
类型约束语法
可以在类型参数后面添加类型约束,通过冒号分割,来作为类型参数链的一部分。代码示例:
class SomeClass{
}
protocol SomePotocol{
}
func someFunction<T:SomeClass,U:SomePotocol>(someT:T,someU:U){
}
类型约束行为
func findIndex<T>
(array:[T],valueOfFind:T) -> Int?{
for (index,value)
in array.enumerated() {
if value == valueOfFind {
// error: binary operator
'==' cannot be applied to two
'T' operands
return index
}
}
return nil
}
由于上述例子,类型参数没有任何约束,当然也不遵守Equatable协议,因此也就不能进行比较。对类型参数添加约束行为Equatable,如下述代码所示:
func findIndex<T:Equatable>(array:[T],valueOfFind:T) -> Int?{
for (index,
value)
in array.enumerated() {
if value == valueOfFind {
return index
}
}
return nil
}
findIndex(array: [
3.0,
4.0,
5.0], valueOfFind:
4.0)
关联类型
关键字associatedtype,关联类型。关键字typealias,类型别名。
关联类型行为
代码示例:
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(
item:Int){
items.append(
item)
}
mutating func pop() -> Int{
return items.removeLast()
}
typealias ItemType = Int
mutating func append(_
item:ItemType){
self.push(
item:
item)
}
var count: Int{
return items.count
}
subscript (i:Int)->Int{
return items[i]
}
}
struct Stack<T>:Container{
var
items = [T]()
mutating func push(
item:T){
items.append(
item)
}
mutating func pop()->T{
return items.removeLast()
}
typealias ItemType = T
mutating func append(_
item: T) {
self.push(
item:
item)
}
var count: Int{
return items.count
}
subscript(i:Int)->T{
return items[i]
}
}
扩展一个存在的类型为指定关联类型
由于数组实现了上述协议内的所有内容,所以定义了扩展之后,可以将Array当做Container来使用。实现空扩展如下:
extension Array:Container{}
Where语句
确保你定义关于类型参数的需求和泛型函数或类型有关联。Where之前,函数的声明。Where之后,某个类型参数的要求,以及类型参数之间的关系。代码示例:
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
}
上述代码的类型参数为:<C1:Container,C2:Container> 函数的参数以及返回值 where C1.ItemType == C2.ItemType,C1.ItemType:Equatable>
C1必须实现Container协议。C2必须实现Container协议。C1的ItemType与C2的ItmeType相同,C1.ItemType == C2.ItemType。C1的ItemType必须实现Equatable协议,C1.ItemType:Equatable。