Swift 的泛型与 Any

在 Swift 中,泛型 和 Any 都能用于定义接受不同类型的参数,在一些代码中,会同时定义 foo(a: T, b: Any),这时真是一脸黑人问号。本文来探究一下泛型 和 Any 的具体区别,先说结论:

泛型的类型检查由编译器负责,Any 则避开了类型系统。

具体是什么意思呢?我们来看下面的例子:

    struct Demo<T> {
        var array1:[T] = []
        var array2:[Any] = []
        init() {}
    }

这个结构体被声明为泛型,成员 array1 要求泛型,array2 要求为 Any,使用时,我们就能看出差别:


原因是,使用 Demo 时,已经将泛型具化为 String 类型,array1 就被同时具化为 String 类型,因此无法放入 Int 类型,而声明为 Any 就没有这样类型约束。
为了对两者安全性有更直观的了解,下面是个更“直白”的例子,我们声明一个返回自己的函数:

func returnSelf(s:Any) -> Any {
    return s
}

然后我们把 return s 改为 return 0 ,会发生什么?


函数依然能运行,这时输入任何字符串都被改为了 Int 类型 0 ,没有任何警告,如果改为泛型版本:


编译器会给出错误提示,在编写的时候,你就能避免这种意外。
实际上, Any 类型就是用来代替 Objective-C 中的 id 类型,让集合类型可以同时存放不同类型,如果你喜欢探究 Swift 的演变历史, 就知道与 Any 相近的还有一个 AnyObject , Swift 2 时,id 被默认映射为 AnyObject ,用来表示任意 NSString、NSNumber 这样的类,可是 Swift 中, String、Int 等都是值类型,无法放入 声明为 AnyObject 的数组中,于是有了 Any ,可以同时存放类和值等任意类型。

使用建议:

现在你知道了 泛型 与 Any 的本质不同,就能够正确应用两者了,当你想要一个通用函数,应该使用泛型,兼容不同类型的同时,不用牺牲类型安全;如果你需要一个混合不同类型的集合,应该使用 Any ,你得自己判断每个元素的类型,但千万小心,这往往是设计不良的表现。

推荐阅读更多精彩内容