3.9 泛型形参与实参
5 分钟阅读
3.9 泛型形参与实参
把声明泛化,从而把具体类型抽象出去。
本章介绍泛型类型、函数和构造器的形参与实参。声明泛型类型、函数、下标或构造器时,你要指定这个泛型类型、函数或构造器可以处理的类型形参。这些类型形参充当占位符,在创建泛型类型实例或调用泛型函数、构造器时,会被实际的具象类型实参替换。
关于 Swift 中泛型的概览,参见泛型。
泛型形参子句
泛型形参子句指定泛型类型或函数的类型形参,以及对这些形参的任何关联约束与要求。泛型形参子句括在尖括号(<>)中,形式如下:
| |
泛型形参列表是用逗号分隔的泛型形参列表,每个泛型形参的形式如下:
| |
一个泛型形参由类型形参和可选的约束组成。类型形参就是占位类型的名字(例如 T、U、V、Key、Value 等)。在该类型、函数或构造器声明的其余部分中——包括函数或构造器的签名中——你都可以使用这些类型形参(以及它们的任何关联类型)。
约束指定类型形参继承自某个特定类,或者遵循某个协议或协议组合。例如在下面的泛型函数中,泛型形参 T: Comparable 表示任何替换类型形参 T 的类型实参都必须遵循 Comparable 协议。
| |
由于 Int 和 Double 等类型都遵循 Comparable 协议,这个函数可以接受这两类实参。与泛型类型不同,使用泛型函数或构造器时不需要指定泛型实参子句,类型实参会根据传给函数或构造器的实参类型推断出来。
| |
泛型形参列表中最后一个泛型形参后面可以跟一个可选的逗号。
整型泛型形参
整型泛型形参充当整数值的占位符,而不是类型的占位符,其形式如下:
| |
其中类型必须是 Swift 标准库中的 Int 类型,或者解析为 Int 的类型别名或泛型类型。
你为整型泛型形参提供的值,必须是整数字面量,或者外围泛型上下文中的另一个整型泛型形参。例如:
| |
类型上的整型泛型形参的值,可以作为该类型的静态常量成员访问,可见性与类型本身相同;函数上的整型泛型形参的值,可以在该函数内部作为常量访问。在表达式中使用时,这些常量的类型是 Int。
| |
整型泛型形参的值可以从你初始化该类型或调用该函数时所用实参的类型推断出来。
| |
泛型 where 子句
你可以在类型或函数体开头的花括号之前写一个泛型 where 子句,为类型形参及其关联类型指定附加要求。泛型 where 子句由 where 关键字加上一条或多条要求组成,要求之间用逗号分隔。
| |
泛型 where 子句中的要求指定某个类型形参继承自某个类,或者遵循某个协议或协议组合。尽管泛型 where 子句为表达类型形参上的简单约束提供了语法糖(例如 <T: Comparable> 等价于 <T> where T: Comparable,等等),它也可以用来对类型形参及其关联类型施加更复杂的约束。例如,你可以约束类型形参的关联类型遵循某些协议。比方说,<S: Sequence> where S.Iterator.Element: Equatable 指定 S 遵循 Sequence 协议,并且关联类型 S.Iterator.Element 遵循 Equatable 协议,这一约束确保序列中的每个元素都是可比较相等性的。整型泛型形参不能有协议或父类要求。
你还可以用 == 运算符指定两个类型完全相同这一要求。例如,<S1: Sequence, S2: Sequence> where S1.Iterator.Element == S2.Iterator.Element 表达这样的约束:S1 和 S2 都遵循 Sequence 协议,并且两个序列的元素必须是同一类型。对于整型泛型形参,== 运算符指定的是对它们取值的要求:你可以要求两个整型泛型形参的值相同,也可以要求某个整型泛型形参取特定的整数值。
任何替换类型形参的类型实参都必须满足施加在该类型形参上的所有约束与要求。
泛型 where 子句可以出现在带类型形参的声明中,也可以出现在嵌套于带类型形参声明内部的声明中。嵌套声明的泛型 where 子句仍然可以引用外围声明的类型形参;不过该 where 子句中的要求只作用于写有它的那个声明。
如果外围声明也有 where 子句,两个子句中的要求会合并。在下面的例子里,只有当 Element 同时遵循 SomeProtocol 和 Numeric 时,startsWithZero() 才可用。
| |
你可以通过在类型形参上提供不同的约束、要求或两者,来重载泛型函数或构造器。调用被重载的泛型函数或构造器时,编译器利用这些约束来决定调用哪个重载版本。
关于泛型 where 子句的更多内容,以及泛型函数声明中的示例,参见泛型 where 子句。
Grammar of a generic parameter clause:
generic-parameter-clause →
<generic-parameter-list,?>
generic-parameter-list → generic-parameter | generic-parameter,generic-parameter-list
generic-parameter → type-name
generic-parameter → type-name:type-identifier
generic-parameter → type-name:protocol-composition-type
generic-parameter →lettype-name:type \generic-where-clause →
whererequirement-list
requirement-list → requirement | requirement,requirement-list
requirement → conformance-requirement | same-type-requirementconformance-requirement → type-identifier
:type-identifier
conformance-requirement → type-identifier:protocol-composition-type
same-type-requirement → type-identifier==type
same-type-requirement → type-identifier==signed-integer-literal
泛型实参子句
泛型实参子句指定泛型类型的类型实参。泛型实参子句括在尖括号(<>)中,形式如下:
| |
泛型实参列表是用逗号分隔的类型实参列表。类型实参是某个具象类型的名字,用来替换泛型类型形参子句中对应的类型形参——对于整型泛型形参而言,则是一个替换该整型泛型形参的整数值。结果就是该泛型类型的一个特化版本。下面的例子展示了 Swift 标准库中泛型字典类型的一个简化版本。
| |
泛型 Dictionary 类型的特化版本 Dictionary<String, Int> 是把泛型形参 Key: Hashable 和 Value 替换为具象类型实参 String 和 Int 得到的。每个类型实参都必须满足它所替换的泛型形参的全部约束,包括泛型 where 子句中指定的任何附加要求。在上面的例子里,类型形参 Key 被约束为遵循 Hashable 协议,因此 String 也必须遵循 Hashable 协议。
你也可以用本身是某个泛型类型特化版本的类型实参来替换类型形参(只要它满足相应的约束与要求)。例如,你可以把 Array<Element> 中的类型形参 Element 替换为数组的特化版本 Array<Int>,从而得到元素本身是整数数组的数组。
| |
泛型实参列表中最后一个类型实参后面可以跟一个可选的逗号。
如泛型形参子句中所述,泛型函数或构造器的类型实参不使用泛型实参子句来指定。
Grammar of a generic argument clause:
generic-argument-clause →
<generic-argument-list,?>
generic-argument-list → generic-argument | generic-argument,generic-argument-list
generic-argument → type | signed-integer-literal