泛型能让我们使用的类型和函数使用未知的类型,许多内置类型,例如可空类型,数组和字典都是用泛型实现的。
泛型类型
下面的结构体实现了一个简单的栈,可以对Int类型执行push和pop操作。
struct Stack{
var items = [Int]()
mutating func push(newitem:Int){
items.append(newitem)
}
mutating func pop() ->Int?{
guard !items.isEmpty else{
return nil
}
return items.removeLast()
}
}
var stack=Stack()
stack.push(newitem:1)
stack.push(newitem:2)
print(stack.pop())
print(stack.pop())
但是如果我们需要对其他类型进行入栈和出栈操作,那就得再编写一个栈类型,使用泛型可以让这个栈支持任意类型。改写后的代码如下,Swift的泛型语法和Java差不多。
struct Stack<T>{
var items = [T]()
mutating func push(newitem:T){
items.append(newitem)
}
mutating func pop() ->T?{
guard !items.isEmpty else{
return nil
}
return items.removeLast()
}
}
var stack=Stack<Int>()
stack.push(newitem:1)
stack.push(newitem:2)
print(stack.pop())
print(stack.pop())
多个占位类型
泛型可支持多个占位类型,代码如下。
struct Stack<T,U,X>{
var items = [T]()
var items1 = [U]()
var items2 = [X]()
...
}
var stack=Stack<Int,String,Double>()
泛型函数和方法
泛型函数需要在函数参数列表前声明占位类型,然后可以在参数和返回值中使用这些占位类型。
func getSelf<T>(arg:T)->T{
return arg
}
print(getSelf(arg: "hello world!"))
类型约束
有时候我们需要对传入的类型做一些限制,可以通过类型约束实现,有两种约束:一种是类型必须是给定类的子类,还有一种是类型必须符合一个协议。
func checkEqual<T:Equatable>(first:T,second:T)->Bool{
return first==second
}
print(checkEqual(first: 1, second: 1))
print(checkEqual(first: "123", second: "456"))
当占位类型比较复杂时,我们还可以使用where语句进一步约束占位类型,如下代码要求newitems的元素必须是T类型。
struct Stack<T>{
var items = [T]()
mutating func push(newitem:T){
items.append(newitem)
}
mutating func pushAll<S:Sequence>(newitems:S)
where S.Iterator.Element==T {
for item in newitems {
self.push(newitem: item)
print("push \(item)")
}
}
mutating func pop() ->T?{
guard !items.isEmpty else{
return nil
}
return items.removeLast()
}
}
var stack=Stack<Int>()
stack.pushAll(newitems:[1,2,3,4,5,6])
关联类型协议
协议不支持泛型,但是支持另外一种特性:关联类型,在协议内部可以使用associatedtype关键字声明一个占位类型,符合这个协议的类型应该在其内部为占位类型提供typealias定义。
protocol InteratorProtocol {
associatedtype Element
mutating func next() -> Element?
}
struct Stack<T>:InteratorProtocol {
typealias Element = Int
mutating func next() -> Element? {
return 0
}
}