Swift 泛型1

207 阅读2分钟

「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」。

本文主要介绍泛型及其底层原理

泛型

泛型主要用于解决代码的抽象能力 + 代码的复用性

例如下面的例子,其中的T就是泛型

func test<T>(_ a: T, _ b: T)->Bool{
    return a == b
}

//经典例子swap,使用泛型,可以满足不同类型参数的调用
func swap<T>(_ a: inout T, _ b: inout T){
    let tmp = a
    a = b
    b = tmp
}

类型约束

在一个类型参数后面放置协议或者是类,例如下面的例子,要求类型参数T遵循Equatable协议

func test<T: Equatable>(_ a: T, _ b: T)->Bool{
    return a == b
}

当传入的参数是没有遵循Equatable协议时,会报错

关联类型

在定义协议时,使用关联类型协议中用到的类型起一个占位符名称

  • 此时的数组中的类型是Int
struct CJLStack {
    private var items = [Int]()

    mutating func push(_ item: Int){
        items.append(item)
    }

    mutating func pop() -> Int?{
        if items.isEmpty {
            return nil
        }
        return items.removeLast()
    }
}
  • 如果想使用其他类型呢?可以通过协议来实现
protocol CJLStackProtocol {
    //协议中使用类型的占位符
    associatedtype Item
}
struct CJLStack: CJLStackProtocol{
    //在使用时,需要指定具体的类型
    typealias Item = Int
    private var items = [Item]()

    mutating func push(_ item: Item){
        items.append(item)
    }

    mutating func pop() -> Item?{
        if items.isEmpty {
            return nil
        }
        return items.removeLast()
    }
}

where语句

where语句主要用于 表明泛型需要满足的条件,即限制形式参数的要求,如下所示

//***********3、where语句:表明泛型需要满足的条件
protocol CJLStackProtocol {
    //协议中使用类型的占位符
    associatedtype Item
    var itemCount: Int {get}
    mutating func pop() -> Item?
    func index(of index: Int) -> Item
}
struct CJLStack: CJLStackProtocol{
    //在使用时,需要指定具体的类型
    typealias Item = Int
    private var items = [Item]()

    var itemCount: Int{
        get{
            return items.count
        }
    }

    mutating func push(_ item: Item){
        items.append(item)
    }

    mutating func pop() -> Item?{
        if items.isEmpty {
            return nil
        }
        return items.removeLast()
    }

    func index(of index: Int) -> Item {
        return items[index]
    }
}
/*
 where语句
 - T1.Item == T2.Item 表示T1和T2中的类型必须相等
 - T1.Item: Equatable 表示T1的类型必须遵循Equatable协议,意味着T2也要遵循Equatable协议
 */
func compare<T1: CJLStackProtocol, T2: CJLStackProtocol>(_ stack1: T1, _ stack2: T2) -> Bool where T1.Item == T2.Item, T1.Item: Equatable{
    guard stack1.itemCount == stack2.itemCount else {
        return false
    }

    for i in 0..<stack1.itemCount {
        if stack1.index(of: i) !=  stack2.index(of: i){
            return false
        }
    }
    return true
}

下面这种写法也是可以的

//写法二
protocol CJLStackProtocol {
    //协议中使用类型的占位符
    associatedtype Item
    var itemCount: Int {get}
    mutating func pop() -> Item?
    func index(of index: Int) -> Item
}
struct CJLStack: CJLStackProtocol{
    //在使用时,需要指定具体的类型
    typealias Item = Int
    private var items = [Item]()

    var itemCount: Int{
        get{
            return items.count
        }
    }

    mutating func push(_ item: Item){
        items.append(item)
    }

    mutating func pop() -> Item?{
        if items.isEmpty {
            return nil
        }
        return items.removeLast()
    }

    func index(of index: Int) -> Item {
        return items[index]
    }
}
extension CJLStackProtocol where Item: Equatable{}
  • 当希望泛型指定类型时拥有特定功能,可以像下面这么写(在上述写法二的基础上增加extension)
//当希望泛型指定类型时拥有特定功能,可以像下面这么写
extension CJLStackProtocol where Item == Int{
    func test(){
        print("test")
    }
}
var s = CJLStack()
s.test()

<!--打印结果-->
test
  • 如果将where后的Int改成Double类型,是无法找到test函数的