泛型
注:本文详细讲解泛型,对写一些公共方法,共用的东西的时候很有帮助。想成为一个优秀的程序员吗?这个必须掌握
泛型代码可以让你写出根据自我需求定义、适用于任何类型的,灵活可重用的函数和类型。
下面直接看一个简单的示例 , 泛型其实就是在写的时候不指定是哪种类型 , 只有再用得时候才指定类型 ,这些类型可以给一些限定。例如 Array
Dictionary
等
写一个简单的交换数据的方法,不限定类型
func swapTwoValue<T>(inout a:T,inout _ b:T){
let temp:T = a
a = b
b = temp
}
这里用T
代表一种类型 ,但是没有指定 , 在函数参数和函数体中都可以使用
var a = 10,b = 20
swapTwoValue(&a,&b)
print("\(a),\(b)")//20,10
var c = "hello",d = "world"
swapTwoValue(&c,&d)
print("\(c),\(d)") //world,hello
可以看到用Int
和 String
类型都可以使用此方法
T只代表一种类型 如果传入两种类型就会报错
var s1 = "hello"
var s2 = 10
swapTwoValue(&s1,&s2)// cannot invoke 'swapTwoValue' with an argument list of type '(inout String,inout Int)'
当然你也可以指定两种类型
func dic<k,v>(a:k,_ b:v){ }
我们这里模拟实现一个栈的操作。栈的特点是后进先出
struct IntStack{
var items = [Int]()
mutating func push(item:Int){
items.append(item)
}
mutating func pop(){
items.removeLast()
}
}
var intStack = IntStack();
intStack.push(1);
print(intStack.items) //[1]
这是一个Int类型的 ,我们看下泛型的
struct Stack<T>{
var items = [T]()
mutating func push(item:T){
items.append(item)
}
mutating func pop()->T{
return items.removeLast()
}
subscript(i:Int)->T{
get{
return items[i]
}
set{
items[i]=newValue
}
}
}
var stack = Stack<String>();
stack.push("hello")
stack.push("ww")
print(stack[0]) //hello
stack[1] = "world"
print(stack) //Stack<Swift.String>(items: ["hello","world"]
这里只实现了基本的pop 和push功能
我们也可以扩展我们的泛型 ,而且不需要提供参数类型
extension Stack{
var topItem:T?{
return items.isEmpty ? nil:items[items.count-1]
}
}
var s1 = Stack<Int>();
s1.push(20)
s1.push(19)
print(s1.topItem) //Optional(19)
我们也可以对泛型进行约束
protocol A{}
func func1<T:A>(t:T){ }
这里这个泛型类型必须为协议A的遵循者 , 这里也可以写某个类 代表类的继承者
不是所有的 Swift 中的类型都可以用等式符(==)进行比较。例如,如果你创建一个你自己的类或结构体来表示一个复杂的数据模型,那么 Swift 没法猜到对于这个类或结构体而言“等于”的意思。
不过,所有的这些并不会让我们无从下手。Swift 标准库中定义了一个Equatable
协议,该协议要求任何遵循的类型实现等式符(==)和不等符(!=)对任何两个该类型进行比较。所有的 Swift 标准类型自动支持Equatable
协议。
func findIndex<T: Equatable>(array: [T],valueToFind: T) -> Int? {
for (index,value) in array.enumerate() {
if value == valueToFind {
return index
}
}
return nil
}
let strs = ["sss","aaa","ddd"];
print(findIndex(strs,valueToFind: "ddd")) //2
关联类型
当定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分是非常有用的。一个关联类型给定作用于协议部分的类型一个节点名(或别名)。作用于关联类型上实际类型是不需要指定的,直到该协议接受。关联类型被指定为typealias关键字。
protocol Container{
//声明一个类型 不指定是什么类型
typealias itemType
mutating func append(item:itemType)
subscript(i:Int)->itemType{ get }
}
这里定义一个协议 , 声明一个类型 不指定是什么类型 , 然后有个append有一个itemType类型的参数 , 还有个下标脚本,就是数组的基本操作
struct Quene<T>:Container{
var items = [T]()
//可以推断出是T
mutating func append(item:T)
{
items.append(item)
}
subscript(i:Int)->T{
return items[i]
}
}
这里可以智能推断是typealias itemType
就是这里的泛型T
非常简单 ,使用就不再赘述
我们也可以使用where
字句来约束泛型 ,放在泛型参数后面即可 。
//可以通过where语句来定义约束
func compare<c1:Container,c2:Container
where c1.itemType == c2.itemType,c1.itemType:Equatable>(a:c1,_ b:c2){
//这里就可以为所欲为了
}