我对一方面用于协议的关联类型的语法和另一方面用于泛型类型的语法之间的区别感到困惑。
例如,在Swift中,可以使用以下方式定义通用类型
struct Stack<T> { var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() } }
而使用诸如
protocol Container { typealias 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 } }
是否有某种深层的原因(或者也许是显而易见的,对我来说就迷失了),原因是该语言未采用后一种语法?
在开发人员列表中已对此进行了多次讨论。基本答案是关联类型比类型参数更灵活。虽然您在这里有一个类型参数的特定情况,但很可能会有多个。例如,集合具有元素类型,但也具有索引类型和生成器类型。如果您完全使用类型参数化对它们进行专门化,则您必须谈论诸如此类Array<String, Int, Generator<String>>的事情。(这将允许我创建由Int以外的其他元素下标的数组,该数组可以被认为是一个功能,但同时也增加了很多复杂性。)
Array<String, Int, Generator<String>>
可以跳过所有操作(Java可以这样做),但是然后您可以使用更少的方法来约束类型。实际上,Java在限制类型方面非常有限。在Java中,您的集合不能具有任意索引类型。Scala像Swift一样,通过关联类型扩展了Java类型系统。关联类型在Scala中非常强大。它们也是造成混乱和头发折磨的常见原因。
这种额外的功能是否值得,是一个完全不同的问题,只有时间才能证明。但是关联类型肯定比简单类型参数化更强大。