我对协议(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 是:由具体类型实现的方法或属性。关联类型是一种对此类 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)相关类型中我们只是描述要满足的其他要求。
TLDR
关联类型和泛型类型参数是非常不同的工具:关联类型是一种描述语言,而泛型是一种实现语言。它们的目的非常不同,尽管它们的用途有时看起来很相似(特别是当涉及到乍一看微妙的差异时,例如任何元素类型集合的抽象蓝图与仍然可以具有任何元素的实际集合类型之间的差异)通用元素)。因为它们是非常不同的野兽,所以它们具有不同的语法。
进一步阅读
Swift 团队有一篇关于泛型、协议(protocol)和相关功能的精彩文章 here 。
关于oop - 为什么协议(protocol)的关联类型在 Swift 中不使用泛型类型语法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58477668/