Swift5.0的泛型

189 阅读2分钟

泛型代码让你能根据你所定义的要求,写出可以用于任何类型的灵活的、可复用的函数。泛型是 Swift 最强大的特性之一,很多 Swift 标准库是基于泛型代码构建的。

泛型可以将类型参数化,提高代码复用率,减少代码量。

####泛型函数 函数用了一个“占位类型”,它规定参数 a 和 b 必须都是同一个类型 T (这里姑且叫做 T ),或者说都是 T 所表示的类型。

泛型函数可以用于任何类型。

下面的例子:

func swapValues<T>(_ a: inout T, _ b: inout T) { 
    (a, b) = (b, a)
}

var aa = 10
var bb = 20
swapValues(&aa, &bb)

其中的T,T会自动推断为Int 。T还可以是其他任意类型如String。这样就不必再写一个函数来实现字符串交换了。直接调用swapValues 这个函数即可。

####泛型类型 Swift允许你定义自己的泛型类型。它们是可以用于任意类型的自定义类、结构体、枚举,和 Array、Dictionary 方式类似。

比如,自定义一个类型Stack,实现方便地存储数据到数组。

struct Stack<Element> {
    // Element 为 占位类型,可随便起
    var items = [Element]()

    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

// 创建时,指定了占位类型为 String
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")

####关联类型(Associated Type)

定义一个协议时,有时在协议定义里声明一个或多个关联类型是很有用的。关联类型即,把一个“占位类型”T给协议中用到的类型。直到采纳协议时,才指定T的实际类型。关联类型通过 associatedtype 关键字指定。

protocol Stackable {
    associatedtype Element // 关联类型 ,这时未确定具体是什么类型
    mutating func push(_ element: Element)  
    mutating func pop() -> Element
    func top() -> Element
    func size() -> Int
}

class StringStack : Stackable {
    // 给关联类型 设为 String
    typealias Element = String    // typealias关键字,给类型起 别名
    var elements = [String]()
    func push(_ element: String) { 
        elements.append(element) 
    } 
    func pop() -> String { 
        elements.removeLast() 
    }
    func top() -> String { 
        elements.last! 
    }
    func size() -> Int { 
        elements.count 
    }
}

var ss = StringStack()
ss.push("Jack")
ss.push("Rose")

协议中也可以声明多个关联类型

####类型约束 指出一个类型形式参数必须继承自特定类,或者遵循一个特定的协议、组合协议。

protocol Runnable { 

}

class Person { 
    // 指出,函数形参的类型必须继承自Person 遵守Runnable协议
    func swapValues<T : Person & Runnable>(_ a: inout T, _ b: inout T) {
        (a, b) = (b, a)
    }
}

可选项的本质是enum类型

public enum Optional<Wrapped> : ExpressibleByNilLiteral { 
    case none
    case some(Wrapped)
    public init(_ some: Wrapped) 
}

var age: Int? = 10
var age0: Optional<Int> = Optional<Int>.some(10) var age1: Optional = .some(10)
var age2 = Optional.some(10)
var age3 = Optional(10)
age = nil
age3 = .none