有人可以向我解释为什么示例 1 可以编译但示例 2 没有吗?
示例 1:
trait Foo[+A]
trait Bar[A] extends Foo[A]
示例 2:
trait Foo[A[+_]]
trait Bar[A[_]] extends Foo[A]
示例 2 无法编译并出现以下错误消息:“类型参数 (A) 的种类不符合 trait Foo 中类型参数 (类型 A) 的预期类型。A 的类型参数与类型 A 的预期参数不匹配:输入
trait Bar) 是不变的,但类型 _ (在 trait Foo 中) 被声明为协变”
最佳答案
在示例 1 中, +A
不受 A 的约束.任何类型都可以是 Foo 的参数。它是对 Foo
的约束。 .表示在Foo
的界面中, A
只能出现在协变位置(简而言之,它可能是方法结果,但不是方法参数)。
有Bar
not covariant 表示 Bar 的接口(interface)不满足相同的约束(或者至少不做广告),所以可能在 Bar
, 一个带有 A
的方法添加了参数。这很常见。例如有 collection.Seq[+A]
, 并由 collection.mutable.Seq[A]
扩展.这不会造成不健全的问题。如果 Y <: X
, 一个 Bar[Y]
不是 Bar[X]
, 但它仍然是 Foo[X]
.
另一方面,在示例 2 中, A[+_]
是对 A
的约束 .只有具有协变类型参数的类型才可以是 Foo 的参数。 Foo
的代码可能会利用该约束,例如分配 A[String]
到 A[Any]
Foo
的代码中的某处.
然后允许 Bar
用非协变类型实例化是不合理的。继承自 Foo
的代码仍然可以调用并分配 A[String]
到A[Any]
, 当 A
不再是协变的。
只要您允许解除对通用参数的约束,就会发生非常相似的可靠性问题。假设您有 Foo[X <: Ordered[X]]
(或 Foo[X : ordering]
)并且您有一个方法 sort
在 Foo
.那么假设Bar[X] extends Foo
被允许。 Bar
的代码中可能没有任何内容需要 X
订购,但仍然,sort
将是可调用的,并且肯定会与无法订购的项目行为不端。
关于您在 Traversable[+Elem, +Col[+_]] 的评论中的问题,并将其扩展为创建一个可变类:
从技术上讲,是的,您可以扩展它并添加一些 var youCanMutateThat : Elem = _
里面。我想这不是你要找的。但我想你的计划是允许使用带有可变 Col 的扩展,我认为你不能这样做,原因如上所述。但是,为什么你首先有 Col[+_] 约束?
关于scala - 子类型中的压倒性差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22533184/