Swift里面有个很强大的特性叫泛型。
泛型代码让你能根据自定义的需求,编写出适用于任意类型的、灵活可复用的函数及类型。你可避免编写重复的代码,而是用一种清晰抽象的方式来表达代码的意图。
泛型的使用场景
文中给出了两个经典的例子:交换两个变量的值和栈的操作。
首先我们看交换两个Int类型变量的值:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
很简单,假若现在让你交换两个Double或String类型的值呢。若没泛型,我们是不是要写多个函数或在一个函数里面要写多个if判断来支持,这样一来显得既冗余也不清晰,也不简洁。
我们再来看看栈的操作:(以Int型栈举例)
struct IntStack {
var items: [Int] = []
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
也很简单,但现在让你对String或Double类型的集合进行相关栈操作。你又该怎么做,是不是也是要和上面一样。很麻烦对吧。
上面两个例子都能很好的用泛型来解决,代码如下:
// 泛型 交换两个值; T为占位符类型名;T所代表的类型由传入的值的类型推断出来
public static func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
struct Stack<Element> {
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
泛型的类型约束
上面两个经典例子充分说明泛型的妙用,那泛型是不是没有任何约束了,在任何场景下都能给任何类型使用呢?答案是NO。有的场景是需要约束的,就像我们现实生活中一样,自由其实相对的,不管是开车等红绿灯,还是人多买东西的时候要排队...至少现实中的一切自由都是建立你所处的国家法律之上的。正应了那句话--无规矩不成方圆。
现在让我们来看个例子,通过一个函数找出数组中某个值的索引。(例如String数组)
func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
将它支持泛型是这样的:
func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
但当你敲完代码会发现,编译报错了。是的不是所有的类型都可以用等式符比较,一些自定义类或结构体,若没有遵循Equatable协议,是无法进行等式比较的。
添加泛型约束,改成如下OK
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}