Swift 泛型
hudson 译 原文
Swift 允许我们创建不与任何特定具体类型绑定的泛型类型、协议和函数,与满足一组给定要求的任何类型一起使用。
作为一种强类型安全的语言,泛型是 Swift 的一个核心特性,对于 Swift 的许多方面都非常重要 —— 包括其标准库,后者大量使用了泛型。只需看看一些基本数据结构,比如 Array
和 Dictionary
,这两者都是泛型的。
泛型使得同一种类型、协议或函数能够针对大量用例进行特化。例如,由于 Array
是泛型的,它允许为任何类型创建专门的实例 —— 比如字符串:
1 | swift复制代码var array = ["One", "Two", "Three"] |
要创建自己的泛型,只需定义泛型类型是什么,以及可选的附加约束。例如, 下面创建一个 Container
类型,它可以包含任何值,以及一个日期:
1 | swift复制代码struct Container<Value> { |
就像能够创建特定的Array
和Dictionary
一样,我们也可以为任何类型的值创建特定Container
,比如字符串或整数:
1 | swift复制代码let stringContainer = Container(value: "Message", date: Date()) |
请注意,在上面不需要指定要将 Container
特定的哪些具体类型 —— Swift 的类型推断会自动推断出stringContainer
是 Container<String>
实例,而 intContainer
是Container<Int>
实例。
当编写可能适用于许多不同类型的代码时,泛型尤其有用。例如,我们可能会使用上面的 Container
来实现一个泛型的 Cache
,它可以为任何类型的键存储任何类型的值。在这种情况下,还需要添加了一个约束:Key
需要遵循Hashabl
e,以便可以将其用于字典 —— 就像这样:
1 | swift复制代码class Cache<Key: Hashable, Value> { |
有了以上内容,现在可以为任何类型创建类型安全的缓存 —— 例如用户或搜索结果:
1 | swift复制代码class UserManager { |
上面的代码中,我们确实需要指定
Cache
具体化的类型,因为编译器无法从调用点推断出该信息。
单个函数无论在何处定义也可以是泛型的。例如,下面代码扩展 String
(它不是泛型类型),以添加一个泛型函数,该函数允许轻松地增加数组中所有 Identifiable
值的 ID:
1 | swift复制代码extension String { |
甚至协议也可以是泛型的!实际上,上面的 Identifiable
协议就是一个例子,因为它使用关联类型
来使其能够用任何类型的 ID
类型进行具体化—— 就像这样:
1 | swift复制代码protocol Identifiable { |
上面的方法使得每个符合 Identifiable
协议的类型都可以决定它想要使用的 ID
类型 —— 同时仍然能够充分利用我们为 Identifiable
类型编写的所有泛型代码(比如上面的 String
扩展)。
例如,这里是 Article
类型使用 UUID
值作为 ID
,而 Tag
类型可能只是使用整数(作为ID
):
1 | swift复制代码struct Article: Identifiable { |
上面的技术非常有用。例如与另一个系统(如服务器端后端)兼容时,需要某些数据模型使用特定类型的
ID
。
再次,上述代码中,编译器将完成大部分繁重的工作,因为它将根据每个类型的 id
属性自动推断出 Article.ID
表示 UUID
,Tag.ID
表示 Int
—— 现在,Article
和 Tag
都可以传递给接受符合 Identifiable
的值的任何函数,同时仍然保持不同的类型,甚至使用自己的不同类型的标识符。
这就是泛型的强大之处,它使我们能够编写更容易重用的代码,同时仍然能够进行局部具体化。算法、数据结构和实用工具通常是泛型的最佳候选 —— 因为它们通常只需要其使用类型满足一定的要求,而不是与特定的具体类型绑定。
感谢阅读!🚀
本文转载自: 掘金