diff --git a/source/chapter2/23_Generics.md b/source/chapter2/23_Generics.md index f45c5a49..da678988 100644 --- a/source/chapter2/23_Generics.md +++ b/source/chapter2/23_Generics.md @@ -17,6 +17,8 @@ > 3.0:翻译+校对:[chenmingjia](https://github.com/chenmingjia),2016-09-12 > 3.0.1,shanks,2016-11-13 +> 3.1:翻译:[qhd](https://github.com/qhd),2017-04-10 + 本页包含内容: - [泛型所解决的问题](#the_problem_that_generics_solve) @@ -28,6 +30,7 @@ - [类型约束](#type_constraints) - [关联类型](#associated_types) - [泛型 Where 语句](#where_clauses) +- [具有泛型 where 子句的扩展](#extensions_with_a_generic_where_clause) *泛型代码*让你能够根据自定义的需求,编写出适用于任意类型、灵活可重用的函数及类型。它能让你避免代码的重复,用一种清晰和抽象的方式来表达代码的意图。 @@ -536,3 +539,83 @@ if allItemsMatch(stackOfStrings, arrayOfStrings) { ``` 上面的例子创建了一个 `Stack` 实例来存储一些 `String` 值,然后将三个字符串压入栈中。这个例子还通过数组字面量创建了一个 `Array` 实例,数组中包含同栈中一样的三个字符串。即使栈和数组是不同的类型,但它们都遵从 `Container` 协议,而且它们都包含相同类型的值。因此你可以用这两个容器作为参数来调用 `allItemsMatch(_:_:)` 函数。在上面的例子中,`allItemsMatch(_:_:)` 函数正确地显示了这两个容器中的所有元素都是相互匹配的。 + + +## 具有泛型 where 子句的扩展 + +你也可以使用泛型 `where` 子句作为扩展的一部分。基于以前的例子,下面的示例扩展了泛型 `Stack` 结构体,添加一个 `isTop(_:)` 方法。 + +```swift +extension Stack where Element: Equatable { + func isTop(_ item: Element) -> Bool { + guard let topItem = items.last else { + return false + } + return topItem == item + } +} +``` + +这个新的 `isTop(_:)` 方法首先检查这个栈是不是空的,然后比较给定的元素与栈顶部的元素。如果你尝试不用泛型 `where` 子句,会有一个问题:在 `isTop(_:)` 里面使用了 `==` 运算符,但是 `Stack` 的定义没有要求它的元素是符合 Equatable 协议的,所以使用 `==` 运算符导致编译时错误。使用泛型 `where` 子句可以为扩展添加新的条件,因此只有当栈中的元素符合 Equatable 协议时,扩展才会添加 `isTop(_:)` 方法。 + +以下是 `isTop(_:)` 方法的调用方式: + +```swift +if stackOfStrings.isTop("tres") { + print("Top element is tres.") +} else { + print("Top element is something else.") +} +// 打印 "Top element is tres." +``` + +如果尝试在其元素不符合 Equatable 协议的栈上调用 `isTop(_:)` 方法,则会收到编译时错误。 + +```swift +struct NotEquatable { } +var notEquatableStack = Stack() +let notEquatableValue = NotEquatable() +notEquatableStack.push(notEquatableValue) +notEquatableStack.isTop(notEquatableValue) // 报错 +``` + +你可以使用泛型 `where` 子句去扩展一个协议。基于以前的示例,下面的示例扩展了 `Container` 协议,添加一个 `startsWith(_:)` 方法。 + +```swift +extension Container where Item: Equatable { + func startsWith(_ item: Item) -> Bool { + return count >= 1 && self[0] == item + } +} +``` + +这个 `startsWith(_:)` 方法首先确保容器至少有一个元素,然后检查容器中的第一个元素是否与给定的元素相等。任何符合 `Container` 协议的类型都可以使用这个新的 `startsWith(_:)` 方法,包括上面使用的栈和数组,只要容器的元素是符合 Equatable 协议的。 + +```swift +if [9, 9, 9].startsWith(42) { + print("Starts with 42.") +} else { + print("Starts with something else.") +} +// 打印 "Starts with something else." +``` + +上述示例中的泛型 `where` 子句要求 `Item` 符合协议,但也可以编写一个泛型 `where` 子句去要求 `Item` 为特定类型。例如: + +```swift +extension Container where Item == Double { + func average() -> Double { + var sum = 0.0 + for index in 0..