Skip to content

Latest commit

 

History

History
226 lines (173 loc) · 9.46 KB

GenericParametersAndArguments.md

File metadata and controls

226 lines (173 loc) · 9.46 KB

泛型形参和实参

将参数声明抽象化以脱离具体类型。

本节描述泛型类型、泛型函数和泛型构造器的形参和实参。在声明泛型类型、函数或构造器时,需要指定泛型可处理的类型形参。这些类型形参相当于占位符,当实例化泛型类型或调用泛型函数、初始化器时,会被具体的类型实参替换。

关于 Swift 语言中的“泛型”,参阅 doc:Generics.

泛型形参子句

泛型形参子句 指定了泛型类型或函数的类型形参,以及对这些形参的任何相关约束和要求。泛型形参用尖括号(<>)括起来,格式如下:

<<#generic parameter list#>>

多个泛型形参用逗号分开,每个形参的形式如下:

<#type parameter#>: <#constraint#>

泛型形参由一个类型形参和一个可选的约束组成。类型形参只是一个占位符(如 TUVKeyValue等),用来表示类型。在声明类型、函数或构造器时(包括函数或构造器的签名),你可以访问这些类型形参及其任何关联的类型。

约束指定了类型形参要继承自特定类,或遵循某个协议或协议组合。例如:在下面的泛型函数中,泛型形参 T: Comparable 表示任何替代类型形参 T 的类型实参都必须遵循 Comparable 协议。

func simpleMax<T: Comparable>(_ x: T, _ y: T) -> T {
    if x < y {
        return y
    }
    return x
}

IntDouble,因为都遵循 Comparable 协议,所以该函数可以接受这两种类型的参数。与泛型类型不同,调用泛型函数或构造器时,你不需要指定泛型实参子句。泛型实参的类型会根据传给函数或构造器的参数类型推断出来。

simpleMax(17, 42) // T is inferred to be Int
simpleMax(3.14159, 2.71828) // T is inferred to be Double

泛型 Where 子句

你可以通过在类型或函数体的起始大括号前包含一个泛型where子句,来对类型形参及其相关类型指定额外的要求。泛型 where子句由where关键字组成,后跟一个或多个要求,多个要求用逗号进行分隔。

where <#requirements#>

泛型 where 子句中的要求指明类型形参要继承自某个类或遵循某个协议或协议组合。虽然 where 子句为表达类型形参的简单约束提供了语法糖(比如,<T: Comparable> 等同于 where T: Comparable 等),但它可以用于为类型形参及其关联类型提供更复杂的约束。比如,你可以指定类型形参的关联类型遵循某个协议:<S: Sequence> where S.Iterator.Element: Equatable 指定了S遵循 Sequence协议,并且S的关联类型S.Iterator.Element遵循Equatable协议。此约束确保序列中的每个元素都是符合Equatable协议的。

还可以使用 == 运算符来指定两个类型必须相同。例如, <S1: Sequence, S2: Sequence> where S1.Iterator.Element == S2.Iterator.Element,表示 S1 和 S2 都遵循 Sequence 协议,并且两个序列元素的类型必须相同。

任何替代类型形参的类型实参都必须满足对该类型形参指定的所有约束与要求。

where子句可以出现在包含类型形参的声明中,或作为声明的一部分,被嵌套另一个在含有类型形参的声明中。被嵌套的泛型 where子句依然可以指向包围它的声明中的类型形参,然而此时 where子句指定的要求仅用于它被声明的地方。

如果外层的声明也有一个 where 子句,那么两个子句的要求会合并。在下面的示例中,startsWithZero()仅在 Element 同时遵循someProtocolNumeric协议时可用。

extension Collection where Element: SomeProtocol {
    func startsWithZero() -> Bool where Element: Numeric {
        return first == .zero
    }
}

重载泛型函数或构造器,泛型形参子句中的类型形参必须有不同的约束或要求。当调用重载的泛型函数或构造器时,编译器会使用这些约束来决定调用哪个重载的函数或构造器。

更多关于泛型 where 从句的内容和关于泛型函数声明的例子,参阅 doc:Generics#泛型-Where-语句.

泛型形参子句的语法格式:

泛型形参子句< 泛型形参列表 >
泛型形参列表泛型形参 | 泛型形参 , 泛型形参列表
泛型形参类型名称
泛型形参类型名称 : 类型标识符
泛型形参类型名称 : 协议合成类型

泛型 where 子句where 约束列表
要求列表要求 | 要求 , 要求列表
要求一致性要求 | 同类型要求

一致性要求类型标识符 : 类型标识符
一致性要求类型标识符 : 协议合成类型
同类型要求类型标识符 == 类型

泛型实参子句

泛型实参 指定了泛型类型的类型实参。泛型实参用尖括号(<>)包围,格式如下

<<#generic argument list#>>

多个泛型实参用逗号分开。类型实参是实际具体类型的名称,用来替代泛型类型中对应的类型参数,从而得到该泛型类型的特定版本。下面的示例展示了 Swift 标准库中的泛型字典类型的简化版本:

struct Dictionary<Key: Hashable, Value>: Collection, ExpressibleByDictionaryLiteral {
    /* ... */
}

泛型Dictionary的特定版本Dictionary<String, Int>是通过具体的类型实参(StringInt)来替换泛型形参(Key: Hashable 和 Value)而形成的。每个类型实参必须满足它替换的泛型形参的所有约束,包括在泛型where子句中指定的额外要求。在上述例子中,Key类型形参要遵循Hashable协议,因此传入的实参String也要遵循Hashable协议。

还可以用本身就是泛型类型的特定版本的类型实参替代类型形参(假设已满足合适的约束和关联类型要求)。例如,可以将 Array<Element>中的类型形参 Element 替换 Array 泛型专用版本 Array<Int> ,以形成一个元素本身是整数数组的二维数组。

let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

正如在doc:GenericParametersAndArguments#泛型形参子句中提到的,在指定泛型函数或构造器的类型实参时,不能使用泛型实参子句。

泛型实参的语法格式:

generic-argument-clause< generic-argument-list >
generic-argument-listgeneric-argument | generic-argument , generic-argument-list
generic-argumenttype