您的位置:首页 > 移动开发 > Swift

Swift_学习笔记_泛型

2017-09-01 17:15 483 查看
泛型可以使代码更加简洁,避免代码重复,类型检查更加严格。

泛型是根据需求定义的,适用于任何类型的,灵活且可重用的函数和类型。

避免重复的代码,以一种清晰和抽象的方法来表达代码的意图。

占位符的位置在函数名、结构体名等名称的后面,紧随名称。例如:
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) // 30


也支持多个类型参数,命名在尖括号内,用逗号隔开。

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!) // Four


类型约束

类似于字典的键值必须是可哈希化的,这就是一种类型约束。

当创建自定义泛型类型时,可以定义你自己的类型约束。

这些约束要支持泛型编程的强力特征中的多数。

抽象概念如可哈希化具有的类型特征是根据它们的概念特征来界定的,而不是它们的直接类型特征。

类型约束语法

可以在类型参数后面添加类型约束,通过冒号分割,来作为类型参数链的一部分。

代码示例:

class SomeClass{

}
protocol SomePotocol{

}
func someFunction<T:SomeClass,U:SomePotocol>(someT:T,someU:U){
// someT 必须是SomeClass类对象或者子类对象
// someU 必须是SomePotocol的实现者
}


类型约束行为

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)
// 输出1


关联类型

关键字
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 别名
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  swift