24.7. 类型约束 让你能够为泛型函数、下标、类型的类型参数定义一些强制要求。
对关联类型添加约束通常是非常有用的。你可以通过定义一个泛型 where
子句来实现。通过泛型 where
子句让关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 where
关键字紧跟在类型参数列表后面来定义 where
子句,where
子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。你可以在函数体或者类型的大括号之前添加 where
子句。
下面的例子定义了一个名为 allItemsMatch
的泛型函数,用来检查两个 Container
实例是否包含相同顺序的相同元素。如果所有的元素能够匹配,那么返回 true
,否则返回 false
。
被检查的两个 Container
可以不是相同类型的容器(虽然它们可以相同),但它们必须拥有相同类型的元素。这个要求通过一个类型约束以及一个 where
子句来表示:
func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {
// 检查两个容器含有相同数量的元素
if someContainer.count != anotherContainer.count {
return false
}
// 检查每一对元素是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// 所有元素都匹配,返回 true
return true
}
这个函数接受 someContainer
和 anotherContainer
两个参数。参数 someContainer
的类型为 C1
,参数 anotherContainer
的类型为 C2
。C1
和 C2
是容器的两个占位类型参数,函数被调用时才能确定它们的具体类型。
这个函数的类型参数列表还定义了对两个类型参数的要求:
C1
必须符合Container
协议(写作C1: Container
)。C2
必须符合Container
协议(写作C2: Container
)。C1
的Item
必须和C2
的Item
类型相同(写作C1.Item == C2.Item
)。C1
的Item
必须符合Equatable
协议(写作C1.Item: Equatable
)。
前两个要求定义在函数的类型形式参数列表里,后两个要求定义在了函数的泛型 where
分句中。
这些要求意味着:
someContainer
是一个C1
类型的容器。anotherContainer
是一个C2
类型的容器。someContainer
和anotherContainer
包含相同类型的元素。someContainer
中的元素可以通过不等于操作符(!=)来检查它们是否相同。
第三个和第四个要求结合起来意味着 anotherContainer
中的元素也可以通过 !=
操作符来比较,因为它们和 someContainer
中的元素类型相同。
这些要求让 allItemsMatch(_:_:)
函数能够比较两个容器,即使它们的容器类型不同。
allItemsMatch(_:_:)
函数首先检查两个容器元素个数是否相同,如果元素个数不同,那么一定不匹配,函数就会返回 false
。
进行这项检查之后,通过 for-in
循环和半闭区间操作符(..<
)来迭代每个元素,检查 someContainer
中的元素是否不等于 anotherContainer
中的对应元素。如果两个元素不相等,那么两个容器不匹配,函数返回 false。
如果循环体结束后未发现任何不匹配的情况,表明两个容器匹配,函数返回 true
。
下面是 allItemsMatch(_:_:)
函数的示例:
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
var arrayOfStrings = ["uno", "dos", "tres"]
if allItemsMatch(stackOfStrings, arrayOfStrings) {
print("All items match.")
} else {
print("Not all items match.")
}
// 打印“All items match.”
上面的例子创建 Stack
实例来存储 String
值,然后将三个字符串压栈。这个例子还通过数组字面量创建了一个 Array
实例,数组中包含同栈中一样的三个字符串。即使栈和数组是不同的类型,但它们都遵从 Container
协议,而且它们都包含相同类型的值。因此你可以用这两个容器作为参数来调用 allItemsMatch(_:_:)
函数。在上面的例子中,allItemsMatch(_:_:)
函数正确地显示了这两个容器中的所有元素都是相互匹配的。