[Swift]泛型

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 = 2
mySwap(&a, &b)
println(a) // 2
println(b) // 1

// Swift自己的库也含有swap泛型函数
swap(&a, &b) // 又换回来了
println(a) // 1
println(b) // 2

func 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值