Swift Generics的力量-第1部分
泛型函数,泛型类型和类型约束
当他们工作时,您爱他们,当他们不工作时,您恨他们。 😀😀
在现实生活中,每个人都知道仿制药的功效:早上起床,决定喝什么,加满杯子☕️
Swift是一种类型安全的语言。 每当使用类型时,都需要指定它们。 例如,我们需要一个可以处理多种类型的函数。 Swift已经提供了Any
和AnyObject
但是除非必须使用,否则最好不要使用它们。 使用Any
和AnyObject
将使我们的代码易碎,因为在编译期间我们将无法捕获类型不匹配的情况。 泛型是我们需求的解决方案。
通用代码允许您编写可重用的函数和数据类型,这些函数和数据类型可与任何与您定义的约束匹配的类型一起使用,同时提供编译时类型安全性。 它使您可以编写避免重复的代码,并以清晰抽象的方式表达其意图。 例如,诸如Array, Set and Dictionary
类型的类型在该元素上是通用的。
假设我们必须打印整数和字符串数组。 我可以创建2个函数来完成这项工作。
让intArray = [1,2,3,4]
让stringArray = [a,b,c,d]
func printInts(array:[Int]){
print(intArray.map {$ 0})
}
func printStrings(array:[String]){
打印(stringArray.map {$ 0})
}
现在,我必须打印浮点数数组或自定义对象的数组。 如果我们看一下上述功能,则所使用的类型只是区别。 因此,无需重复代码,我们可以编写可重用的泛型函数。
泛型函数可以使用由占位符类型T标识的任何类型。占位符类型名称并没有说明T
必须是什么,但是它确实说两个array
必须是T
类型,无论T
代表什么。 每次调用print(_:)
函数时,都会确定要代替T
使用的实际类型。
func print (array:[T]){
打印(array.map {$ 0})
}
上例中的占位符类型T是类型参数。 通过在尖括号内用逗号分隔多个类型参数名称,可以提供多个类型参数。
如果我们看一下Array 和Dictionary ,它们有一个命名的类型参数,即Element&Key,Value,它说明类型参数与它所使用的泛型类型或函数之间的关系。
注意:始终为类型参数提供大写的驼峰名称(例如T
和TypeParameter
),以表明它们是类型的占位符,而不是值。
这些是可以与任何类型一起使用的自定义类,结构和枚举,类似于Array
和Dictionary
。
让我们创建堆栈
当前,此堆栈仅能容纳Integer元素,如果我必须存储其他类型的元素,则需要创建另一个堆栈或将其转换为通用堆栈。
由于泛型可以是任何类型,因此我们不能做太多事情。 在可与泛型函数和泛型类型一起使用的类型上强制使用类型约束有时很有用。 类型约束指定类型参数必须符合特定协议或协议组成。
例如,Swift的Dictionary
类型对可用作字典键的类型进行了限制。 Dictionary
需要其键是可哈希的,以便它可以检查它是否已经包含特定键的值。
func someFunction (someT:T,someU:U){
//函数体在这里
}
在上面的要点中,我们创建了T类型的堆栈,但无法比较两个堆栈。 由于所有类型都不符合Equatable
。 我们需要修改它以使用Stack
。
让我们举个例子。
func min (_ x:T,_ y:T)-> T {
返回y <x? y:x
}
编译器缺少它需要发出功能代码的两个基本信息:
- 类型T的变量的大小
- 必须在运行时调用<函数的特定重载的地址。
每当编译器遇到具有通用类型的值时,它将在容器中装箱该值。 该容器具有固定的大小来存储值。 如果值太大而无法容纳,Swift会将其分配到堆上,并将对它的引用存储在容器中。
编译器还为每个通用类型参数维护一个或多个见证表的列表:一个是值见证表,再加上针对该类型的每个协议约束的一个协议见证表。 见证表用于在运行时动态地将函数调用分派到正确的实现。
感谢您阅读文章。 该系列的第2部分可在此处获得:https://medium.com/swift-india/power-of-generics-part2-b39f412a1d54
您可以在以下位置找到我:
Linkedin: Aaina Jain
推特: __aainajain