简介
泛型是 Swift 最强大的特性之一,很多 Swift 标准库是基于泛型代码构建的。
在没有泛型的情况下,我们常常需要为不同类型写重复的代码。
func swapInts(_ a: inout Int, _ b: inout Int) {
...
}
func swapStrings(_ a: inout String, _ b: inout String) {
...
}
泛型代码让你能根据你所定义的要求,写出可以用于任何类型的灵活的、可复用的函数。
- 泛型可以将类型参数化,提高代码复用率,减少代码量
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
1.泛型函数
用<T>表明类型可变。至于是具体什么类型,由调用函数时,实参的类型决定。
func makeArray<T>(repeating item: T, count: Int) -> [T] {
return Array(repeating: item, count: count)
}
2.泛型约束
默认泛型参数可以是任何类型,但你可以限制它必须符合某个协议。
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
for (index, element) in array.enumerated() {
if element == value {
return index
}
}
return nil
}
3.泛型类型
- 泛型结构体
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
- 泛型类型
class Box<T> {
var value: T
init(_ value: T) {
self.value = value
}
}
- 泛型枚举
enum Result<Success, Failure> {
case success(Success)
case failure(Failure)
}
4.关联类型(Associated Types)— 协议中的泛型
用在协议中定义一个“占位类型”。具体类型,由协议遵守者决定。
- 例 protocol Container
protocol Container {
associatedtype XO
mutating func append(_ item: XO)
var count: Int { get }
subscript(i: Int) -> XO { get }
}
struct IntStack: Container {
var items: [Int] = []
mutating func append(_ item: Int) {
items.append(item)
}
var count: Int { return items.count }
subscript(i: Int) -> Int {
return items[i]
}
}
- 例 protocol Stackable,使用关键字
associatedtype+typealias
protocol Stackable {
associatedtype Element
mutating func push(_ element: Element)
mutating func pop() -> Element
}
class StringStack : Stackable {
// 给关联类型 设为 String
typealias Element = String
var elements = [String]()
func push(_ element: String) {
elements.append(element)
}
func pop() -> String {
elements.removeLast()
}
}
5.where 语句:添加更复杂的约束
可用于:
- 泛型函数
- 泛型类型扩展
- 协议扩展
func allItemsMatch<C1: Container, C2: Container>(_ a: C1, _ b: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {
if a.count != b.count { return false }
for i in 0..<a.count {
if a[i] != b[i] { return false }
}
return true
}
示例
我们写了一个工具方法,用于加载JSON文件并转化为 User实例
// 传入JSON数据文件名,最后转化为 实例
func decode(_ file: String) -> [String: User] {
// 查找文件路径
guard let url = Bundle.url(forResource: file, withExtension: nil) else {
fatalError()
}
// 根据路径把文件转为Data
guard let data = try? Data(contentsOf: url) else {
fatalError()
}
// 把JSON数据转化为 实例
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode([String: User].self, from: data) else {
fatalError()
}
return loaded
}
如果,我们需要处理除User之外的其他类型,这时就是泛型的好处了。
// 传入JSON数据文件名,最后转化为 实例
func decode<T: Codable>(_ file: String) -> T {
// 查找文件路径
guard let url = Bundle.url(forResource: file, withExtension: nil) else {
fatalError()
}
// 根据路径把文件转为Data
guard let data = try? Data(contentsOf: url) else {
fatalError()
}
// 把JSON数据转化为 实例
let decoder = JSONDecoder()
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError()
}
return loaded
}
注意,此例中T: Codable表示该泛型必须是 遵守 Codable协议的
此外,调用时需要 带上 类型注释,因为此时无法进行 类型推断
let user: [String: User] = Bundle.main.decode("user.json")