一方面,我对用于协议(protocol)关联类型的语法与另一方面用于泛型类型的语法之间的区别感到困惑。
例如,在 Swift 中,可以使用类似的东西定义泛型类型
struct Stack<T> {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
虽然一个人使用类似的东西定义了一个带有关联类型的协议(protocol)
protocol Container {
associatedtype T
mutating func append(item: T)
var count: Int { get }
subscript(i: Int) -> T { get }
}
为什么后者不只是:
protocol Container<T> {
mutating func append(item: T)
var count: Int { get }
subscript(i: Int) -> T { get }
}
语言没有采用后一种语法是否有一些深刻的(或者可能只是显而易见但我迷失了)原因?
最佳答案
RobNapier 的回答(一如既往)非常好,但只是换个角度可能会进一步启发...
关联类型
协议(protocol)是一组抽象的要求——一个具体类型必须满足的 list ,才能表明它符合协议(protocol)。传统上,人们认为该 list 是行为:由具体类型实现的方法或属性。关联类型是一种命名此类检查表中涉及的事物的方式,从而扩展定义,同时保持开放式定义,符合类型如何实现符合性。
当你看到:
protocol SimpleSetType {
associatedtype Element
func insert(_ element: Element)
func contains(_ element: Element) -> Bool
// ...
}
这意味着,对于声明符合 SimpleSetType
的类型,该类型不仅必须包含 insert(_:)
和 contains(_ :)
函数,这两个函数必须采用相同类型的参数。但该参数的类型是什么并不重要。
您可以使用泛型或非泛型类型实现此协议(protocol):
class BagOfBytes: SimpleSetType {
func insert(_ byte: UInt8) { /*...*/ }
func contains(_ byte: UInt8) -> Bool { /*...*/ }
}
struct SetOfEquatables<T: Equatable>: SimpleSetType {
func insert(_ item: T) { /*...*/ }
func contains(_ item: T) -> Bool { /*...*/ }
}
请注意,BagOfBytes
或 SetOfEquatables
都没有定义 SimpleSetType.Element
和用作这两种方法的参数的类型之间的联系 —编译器会自动计算出这些类型与正确的方法关联,因此它们满足协议(protocol)对关联类型的要求。
关于泛型类型参数
关联类型扩展了您创建抽象 list 的词汇量,而泛型类型参数限制了具体类型的实现。当你有一个像这样的泛型类时:
class ViewController<V: View> {
var view: V
}
它并没有说有很多不同的方法来制作一个ViewController
(只要你有一个view
),它说的是一个ViewController
是一个真实的、具体的东西,它有一个 View
。此外,我们不知道任何给定的 ViewController
实例都有什么样的 View ,但我们知道它一定是一个 View
(要么是 View
类,或实现 View
协议(protocol)的类型……我们不说)。
或者换句话说,编写泛型类型或函数是编写实际代码的捷径。举个例子:
func allEqual<T: Equatable>(a: T, b: T, c: T) {
return a == b && b == c
}
这与您遍历所有 Equatable
类型并写下的效果相同:
func allEqual(a: Int, b: Int, c: Int) { return a == b && b == c }
func allEqual(a: String, b: String, c: String) { return a == b && b == c }
func allEqual(a: Samophlange, b: Samophlange, c: Samophlange) { return a == b && b == c }
如您所见,我们在这里创建代码,实现新的行为——这与协议(protocol)关联类型非常不同,在协议(protocol)关联类型中我们只描述要满足的其他要求。
长见识
关联类型和泛型类型参数是两种截然不同的工具:关联类型是一种描述语言,泛型是一种实现语言。它们有非常不同的目的,尽管它们的用途有时看起来相似(尤其是当涉及到乍一看的细微差别时,例如任何元素类型集合的抽象蓝图与仍然可以具有任何元素的实际集合类型之间的差异)通用元素)。因为它们是非常不同的野兽,所以它们具有不同的语法。
进一步阅读
Swift 团队有一篇关于泛型、协议(protocol)和相关特性的精彩文章 here .
关于oop - 为什么协议(protocol)的关联类型在 Swift 中不使用泛型语法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56923316/