对比TypeScript&Swfit系列---泛型

958 阅读2分钟

TypeScript

用泛型去修饰一个函数,类或者协议的变量

function identity<T>(arg: T): T {
    return arg;
}
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
interface GenericIdentityFn<T> {
    (arg: T): T;
} 

泛型约束

有时候需要对变量类型或者行为有所约束

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
}

一个比较复杂的例子如下:

class BeeKeeper {
    hasMask: boolean;
}

class ZooKeeper {
    nametag: string;
}

class Animal {
    numLegs: number;
}

class Bee extends Animal {
    keeper: BeeKeeper;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

function findKeeper<A extends Animal, K> (a: {new(): A;
    prototype: {keeper: K}}): K {

    return a.prototype.keeper;
}

findKeeper(Lion).nametag;  // typechecks!

findKeeper这个函数定义入参a要有构造函数,返回的类型为A,A约束要继承Animal,同时a要有keeper的prototype,类型为K,如果对K有约束可继续添加K extends

Swift

常规操作,可以修饰类,结构体和函数

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

同样你在catgory中也可以使用class或者struct定义的泛型类型

extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

但是在协议中定义泛型类型的方式会比较特殊和TS不同

在swift语言中,协议中的泛型有个名称叫关联类型,用associatedtype修饰。其实也就是一个占位符。

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

在实际的过程中,编译器会自动推导出Item的具体类型。比如:

struct IntStack: Container {
    // IntStack 的原始实现部分
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
    // Container 协议的实现部分
    typealias Item = Int
    mutating func append(_ item: Int) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}

会自动推导出Item是Int类型,无需写typealias Item = Int这句。

泛型约束

添加约束也很简单,和TS一样。

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 这里是泛型函数的函数体部分
}
protocol Container {
    associatedtype Item: Equatable
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

如果对协议约束有更多详细的描述,可使用where语句

func allItemsMatch<C1: Container, C2: Container>
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.Item == C2.Item, C1.Item: Equatable {
        // 检查两个容器含有相同数量的元素
        if someContainer.count != anotherContainer.count {
            return false
        }
        // 检查每一对元素是否相等
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        // 所有元素都匹配,返回 true
        return true
}

该方法约束C1和C2要满足Container协议,同时后面的where语句要求C1.Item == C2.Item,C1.Item满足Equatable协议。在category中还可以扩展约束的要求。

extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        guard let topItem = items.last else {
            return false
        }
        return topItem == item
    }
}

扩展了Element必须满足Equatable协议。