泛型基础知识
泛型是类型的占位符,允许你编写可以跨多种类型应用的灵活代码。使用泛型优于 Any
的优点是它们仍然允许编译器强制实现强类型安全性。
通用占位符在尖括号 <>
中定义。
通用函数
对于函数 ,此占位符位于函数名称后面:
/// Picks one of the inputs at random, and returns it
func pickRandom<T>(_ a:T, _ b:T) -> T {
return arc4random_uniform(2) == 0 ? a : b
}
在这种情况下,通用占位符是 T
。当你来调用函数时,Swift 可以为你推断 T
的类型(因为它只是作为实际类型的占位符)。
let randomOutput = pickRandom(5, 7) // returns an Int (that's either 5 or 7)
这里我们将两个整数传递给函数。因此 Swift 推断 T == Int
- 因此函数签名被推断为 (Int, Int) -> Int
。
由于泛型提供了强大的类型安全性 - 函数的参数和返回必须是相同的类型。因此以下内容不会编译:
struct Foo {}
let foo = Foo()
let randomOutput = pickRandom(foo, 5) // error: cannot convert value of type 'Int' to expected argument type 'Foo'
通用类型
为了将泛型与类 ,结构或枚举一起使用 ,可以在类型名称后定义通用占位符。
class Bar<T> {
var baz : T
init(baz:T) {
self.baz = baz
}
}
当你使用 Bar
类时,此通用占位符将需要一个类型。在这种情况下,可以从初始化 init(baz:T)
推断出。
let bar = Bar(baz: "a string") // bar's type is Bar<String>
这里通用占位符 T
被推断为 String
类型,因此创建了 Bar<String>
实例。你还可以明确指定类型:
let bar = Bar<String>(baz: "a string")
与类型一起使用时,给定的通用占位符将在给定实例的整个生命周期内保持其类型,并且在初始化后不能更改。因此,当你访问属性 baz
时,对于此给定实例,它将始终为 String
类型。
let str = bar.baz // of type String
传递通用类型
当你来传递泛型类型时,在大多数情况下,你必须明确你期望的通用占位符类型。例如,作为函数输入:
func takeABarInt(bar:Bar<Int>) {
...
}
此功能只接受 Bar<Int>
。尝试传入通用占位符类型不是 Int
的 Bar
实例将导致编译器错误。
通用占位符命名
通用占位符名称不仅限于单个字母。如果给定的占位符表示有意义的概念,则应为其指定一个描述性名称。例如,Swift 的 Array
有一个名为 Element
的通用占位符,它定义了给定 Array
实例的元素类型。
public struct Array<Element> : RandomAccessCollection, MutableCollection {
...
}