[Swift]泛型

来源:互联网 发布:软件项目成本核算 编辑:程序博客网 时间:2024/04/30 13:38

1. Swift泛型的定义方法:

    1) 和C++泛型概念一样,用法和C++也相似,同样也是使用一个类型占位符(代表一个通用类型,也是泛型的参数,用户随意取名,但要求按照标示符命名规则进行,因为其代表一个通用类型,因此和定义其它类型名的规范一样,最好是首字母大写的驼峰命名方式,一般取T);

    2) 一个简单的泛型函数的例子:

func mySwap<T>(inout a: T, inout b: T) {    let t = a    a = b    b = t}var a = 1, b = 2mySwap(&a, &b)println(a) // 2println(b) // 1// Swift自己的库也含有swap泛型函数swap(&a, &b) // 又换回来了println(a) // 1println(b) // 2func myEqual<T: Comparable>(a: T, b: T) -> Bool {    return a == b}
此例子中类型占位符为T,可以当做普通类型一样使用;

    3) 和任何一种语言的泛型一样,都是动态推导类型的,只有当实际传参的时候才会根据参数类型生成相应版本的代码(即运行时动态加载函数代码),因此执行效率较低,但是程序灵活性非常强;

    4) 多类型参数:如果有多个类型占位符,则声明的时候用逗号,隔开

func f<T, K>(a: T, b: K) -> K {    return b}println(f(12, "haha")) // haha


2. 泛型模板类以及扩展模板类:

    1) 和C++模板类的概念一致,在类定义中可以含有类型占位符,用于表示一种通用类型;

!!泛型不仅支持函数,同时也支持结构体和枚举类型!

    2) 定义类模板是需要在类名之后写上占位符,请看如下例子,模拟实现一个栈模板类:

struct Stack<T> {    var items = [T]()        mutating func push(item: T) {        items.append(item)    }        mutating func pop() -> T {        return items.removeLast()    }}

     3) 扩展模板类时不需要重新写泛型参数列表,可以在扩展体中直接使用定义类时的泛型,非常方便:接上例代码

extension Stack {    subscript(index: Int) -> T? {        if index < 0 || index + 1 > countElements(items) {            return nil        }                return items[index]    }}


3. 泛型约束:

    1) 顾名思义,就是要求那个类型占位符所代表的泛型遵守某些规矩,比如必须要遵守某些协议,或者必须由什么类继承而来等;

    2) 实际上只能约束泛型遵守什么协议或者泛型继承自那个类,比如对于比较两个泛型是否内容相等,就不能直接使用普通的泛型,必须使用能遵守Equatable协议的两个泛型才能比较,该协议规定类型必须实现==和!=两种操作符的实现,可以想象,如果不遵守该协议,直接在泛型函数体中使用==操作符比较两个泛型是否相等,如果该泛型传进来的是一个用户自定义的类并且该类没有实现==操作符,那该如何让该泛型函数进行比较呢?请看以下的例子:

func equals<T: Equatable>(a: T, b: T) -> Bool {    return a == b}
如果要求遵守多个协议则用逗号隔开,就和继承类、遵守协议的语法一模一样,比如:

func equals<T: Equatable, Hashable>(a: T, b: T) -> Bool { // 既遵守Equatable协议也遵守Hashable协议    return a == b}
    3) 再来看一个例子,该例子是在一个泛型数组中查找某个元素并返回下标:

func findItemIndexFrom<T: Equatable>(arr: [T], byValue val: T) -> Int? {    for (index, value) in enumerate(arr) {        if value == val {            return index        }    }        return nil}


4. 关联类型——实现“泛型协议”:

    1) Swift不运行定义泛型协议(至少语法上不允许像定义泛型类型那样定义泛型协议),但是有时有这方面的需要,比如在一个协议中指定一种泛型,然后协议中规定了很多方法或属性都需要用到该泛型,但一个类遵守该协议时再根据具体需要规定该泛型的具体类型,同时并使用该具体类型来实现协议中规定的方法和属性;

    2) 答案时肯定的,Swift提供关联类型这种形式来解决以上问题,即可以先用typealias定义一个泛型(即不对该泛型赋值,值定义其名字),然后在规定要实现的属性或方法中使用该泛型,最后是当一个类型遵守该协议时再用typealias对该泛型进行赋值,使其成为某个具体的类型,接着再实现规定的属性和方法时用具体的类型替换该泛型即可:

protocol A {    typealias ItemType // 先指定一个泛型        // 规定要实现的内容中暂时用泛型替代    mutating func push(item: ItemType)    mutating func pop() -> ItemType}struct Stack<T>: A {    var items = [T]()        // 遵守协议时再将泛型和某种具体的类型“相关联”,而该某种具体的类型可以是模板类的类型占位符!    // 可见Swift超级强大    typealias ItemType = T // 同样是可写可不写,编译器能自动推断    mutating func push(item: T) { // 实现时用具体类型代替关联类型        items.append(item)    }        mutating func pop() -> T {        return items.removeLast()    }}struct IntStack: A { // 一个Int的版本    var items = [Int]()        typealias ItemType = Int // 词句可写可不写,编译器都能自动推断    mutating func push(item: Int) {        items.append(item)    }        mutating func pop() -> Int {        return items.removeLast()    }}


5. 用where语句对关联类型进行约束:

    1) 和SQL的where约束类似,该约束发生在指定某个类型遵守某个协议(协议带有关联类型)时使用;

    2) 具体语法如下:

protocol ProtocolStack {    typealias ItemType        mutating func push(item: ItemType)    mutating func pop() -> ItemType    var count: Int { get }    subscript(index: Int) -> ItemType { get }}struct Stack<T>: ProtocolStack {    var items = [T]()        mutating func push(item: T) {        items.append(item)    }        mutating func pop() -> T {        return items.removeLast()    }        var count: Int { return countElements(items) }        subscript(index: Int) -> T {        return items[index]    }}func isAllItemMatched<    STK1: ProtocolStack, STK2: ProtocolStack    where STK1.ItemType == STK2.ItemType, STK1.ItemType: Equatable    >(stk1: STK1, stk2: STK2) -> Bool {        // 该函数用于比较两个栈中的内容是否完全相等        // 因此两个类型都必须遵守ProtocolStack协议        // 并且栈中元素必须遵守可比较协议Equatable        // 元素进行比较的前提是两种栈中元素的类型必须相同,这也就是要求两种关联类型必须相同            if ( stk1.count != stk2.count ) {        return false    }        for i in 0..<stk1.count {        if stk1[i] != stk2[i] {            return false        }    }        return true}
    3) 对以上成果进行使用:泛型类型定义方法和C++类似,如下

var stk1 = Stack<Int>()var stk2 = Stack<Int>()for i in 0..<10 {    stk1.push(i)    stk2.push(i)}println(isAllItemMatched(stk1, stk2)) // true


0 0
原创粉丝点击