Swift: 泛型

2017-09-26  本文已影响22人  伯wen

泛型的概念

泛型代码可根据自定义需求,写出适用于任何类型、灵活且可重用的函数和类型,避免重复的代码,用一种清晰和抽象的思维表达代码的意思

泛型函数

示例:

/// 交换变量的值
func exchange<T>(_ one: inout T, _ two: inout T) {
    (one, two) = (two, one)
}

var a = 10.0
var b = 20.0

print("a = \(a), b = \(b)")     // a = 10.0, b = 20.0
exchange(&a, &b)               // 交换a和b的值
print("a = \(a), b = \(b)")     // a = 20.0, b = 10.0

var c = "hellow"
var d = "world"

print("a = \(c), b = \(d)")     // a = hellow, b = world
exchange(&c, &d)               // 交换c和d的值
print("a = \(c), b = \(d)")     // a = world, b = hellow

注意: 泛型函数在调用的时候, 会根据传入的值推断出对应的类型

func 函数名<占位类型列表>(参数列表) {
    // 函数体
}

类型参数

综上有泛型函数格式如下:
func 函数名<类型参数列表>(参数列表) {
   // 函数体
}

泛型类型

示例
泛型类: 
class GenericClass<Element> {
    // 集合
    var items = [Element]()
    // 压栈
    func push(_ item: Element) {
        items.append(item)
    }
    // 出栈
    func pop() -> Element? {
        return items.isEmpty ? nil : items.removeLast()
    }
}
泛型结构体:
struct GenericStruct<Element> {
    // 集合
    var items = [Element]()
    // 压栈
    mutating func push(_ item: Element) {
        items.append(item)
    }
    // 出栈
    mutating func pop() -> Element? {
        return items.isEmpty ? nil : items.removeLast()
    }
}
泛型枚举:
enum GenericEnum<Element> {
    case none
    case some(Element)
}
// 创建GenericStruct类型的机构体变量struct
// 指定类型参数为 Int
var struct = GenericStruct<Int>()
// 使用struct时, push(_:), pop()方法使用 类型参数的地方 都会替换为Int类型
struct.push(1)
struct.push(2)
struct.push(3)
struct.push("4")   // 报错: 因为push(_:)接收的参数类型已经被替换成Int

let result = struct.pop()   // result = 3
给泛型类型添加分类(extension)
错误一: 分类的定义中不可以增加新的类型参数
extension GenericClass<T> { }
错误二: 分类的定义中不需要写已经有的类型参数
extension GenericClass<Element> { }
extension GenericClass {
    // 使用已有的 类型参数: Element 做为返回值
    func element(at index: Int) -> Element? {
        if index < items.count {
            return items[index]
        }else {
            return nil
        }
    }
}

虽然在分类中无法定义新的类型参数, 但是可以在分类新定义的方法中引入其他的类型参数

extension GenericClass {
   func exchange<T>(one: inout T, two: inout T) {
       (one, two) = (two, one)
   }
}

泛型约束

那么什么是 参数类型添加约束呢?

就拿我们经常使用的Dictionary为例, 我们知道Dictionary的定义中有两个参数类型, 分别为 KeyValue , 而且在给Dictionary添加元素的时候, key的值都是唯一的,即Dictionary根据Key的值来判断是修改还是增加元素, 而Swift中的Dictionary是根据Key的哈希值来判断唯一性的, 也就是说DictionaryKey值必须是可哈希的, 所以Dictionary的类型参数Key有一个约束, 那就是 可哈希的值

public struct Dictionary<Key, Value> : Collection, ExpressibleByDictionaryLiteral where Key : Hashable{}
类型约束语法
泛型函数添加类型约束
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 这里是反省函数的函数体部分
}

泛型类型添加类型约束
class GenericClass<T: SomeClass, U: SomeProtocol> {
    // 类的实现部分
}
类型约束实践
func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
    
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
let strings = ["a", "b", "c", "d", "e"]
if let foundIndex = findIndex(of: "c", in: strings) {
    print("c 的索引值是 \(foundIndex)")
}
// 打印: c 的索引值是 2
func findIndex<T>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

关联类型(泛型协议)

protocol Container {
    associatedtype ItemType
    mutating func append(item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}
struct IntStack: Container {
    // 集合数组, 用于存放元素
    var items = [Int]()
  
    // Container协议部分
    typealias ItemType = Int  // 通过关键字 typealias 指定ItemType的类型为Int
    mutating func append(item: Int) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}
struct IntStack: Container {

    var items = [Int]()

    mutating func append(item: Int) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}

即: 对于泛型协议, 当有类型遵从该协议的时候, 只需要给未确定具体类型的关联类型所参与的所有方法中, 都给出唯一指定类型时, 并不需要特意声明该关联类型的声明也能正常运行, 原因就是Swift的自动推断

struct Stack<Element>: Container {
    var items = [Element]()
    // 由于所有需要关联类型的地方都指定了明确类型, 就不需要在特意的声明关联类型具体是什么类型了, 这里自动推断出 ItemType的类型是Stack对象创建时指定的泛型具体类型
    // typealias ItemType = Element
    mutating func append(item: Element) {
        self.items.append(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Element {
        return items[i]
    }
}
通过扩展一个存在的类型来指定关联类型
struct Stack<Element> {
    var items = [Element]()
}
// 通过扩展遵从泛型协议 Container
extension Stack: Container {
    // 这一行可以不写
    // typealias ItemType = Element

    mutating func append(item: Element) {
        self.items.append(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Element {
        return items[i]
    }
}

泛型 Where 语句

  • 上面的叙述中, 类型约束让我们能够为泛型函数泛型类型类型参数定义一些强制要求
  • 除了类型约束以外, 还有一种方法给泛型函数泛型类型类型参数定义约束, 那就是where子句
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
func findIndex<T>(of valueToFind: T, in array: [T]) -> Int? where T : Equatable {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}
// 容器协议
protocol Container {
    associatedtype ItemType
    
    mutating func append(item: ItemType)
}

func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool {
    // 检查两个容器包含相同数量的元素
    if someContainer.count != anotherContainer.count {
        return false
    }
    // 检查每个元素是否相等
    for i in 0..<someContainer.count {
        if someContainer[i] != anotherContainer[i] {
            return false
        }
    }
    return true
}
// 遵守Container协议的类型Stack1
class Stack1<Element>: Container {
    var items = [ItemType]()
    
    typealias ItemType = Element
    
    func append(item: Element) {
        items.append(item)
    }
}

// 遵守Container协议的类型Stack2
class Stack2<Element>: Container {
    var items = [ItemType]()
    
    typealias ItemType = Element
    
    func append(item: Element) {
        items.append(item)
    }
}
// 这里只考虑定义部分, 不考虑实现部分
func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.ItemType == C2.ItemType {//函数体}

有了这个where子句后, 只有Stack1Stack2中的元素类型必须一致才能使用该函数

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
}

类型的定义中也可以使用where子句添加约束, 用法与在泛型函数中一样, 都写在定义的后面, 大括号{}的前面

class Stack<Element>: Container where Stack.ItemType : Equatable {
    var items = [ItemType]()
    
    typealias ItemType = Element
    
    func append(_ item: Element) {
        items.append(item)
    }
}
具有泛型where子句的扩展
extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        return items.last == item
    }
}
if stackOfStrings.isTop("c") {
    print("Top element is c.")
} else {
    print("Top element is something else.")
}
// 打印 "Top element is c."
struct NotEquatable { }
var notEquatableStack = Stack<NotEquatable>()
let notEquatableValue = NotEquatable()
notEquatableStack.push(notEquatableValue)
notEquatableStack.isTop(notEquatableValue)  // 报错
extension Container where ItemType: Equatable {
    func startWith(_ item: ItemType) -> Bool {
        return count >= 1 && self[0] == item
    }
}

extension Array: Container{}

let array = ["a", "b", "c"]

if array.startWith("a") {
    print("array 第一个元素是 a")
}else {
    print("array 第一个元素不是 a")
}
// 打印 array 第一个元素是 a
extension Container where ItemType == Double {
    func average() -> Double {
        var sum = 0.0
        for i in 0..<count {
            sum += self[i]
        }
        return sum / Double(count)
    }
}
print([1260.0, 1200.0, 98.6, 37.0].average())
// 打印 "648.9"
具有泛型 Where 子句的关联类型
protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
    
    associatedtype Iterator: IteratorProtocol where Iterator.Element == Item
    func makeIterator() -> Iterator
}
protocol ComparableContainer: Container where Item: Comparable {}
上一篇下一篇

猜你喜欢

热点阅读