有人可以向我解释为什么示例1编译但示例2没有编译?
示例1:
trait Foo[+A]
trait Bar[A] extends Foo[A]
示例2:
trait Foo[A[+_]]
trait Bar[A[_]] extends Foo[A]
示例2没有使用以下错误消息进行编译:"类型参数(A)的种类不符合特征Foo中类型参数(类型A)的预期种类。 A类型参数与A类预期参数不匹配:类型_(in trait Bar)是不变的,但是_(在trait Foo中)被声明为covariant"
答案 0 :(得分:7)
在示例1中, +A
不是对A 的约束。任何类型都可以是Foo的参数。这是Foo
的约束。这意味着在Foo
的界面中,A
只能出现在协变位置(很快,它可能是方法结果,但不是方法参数)。
使Bar
不协变意味着Bar的接口不满足相同的约束(或者至少不会通告它),因此可能在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 [+ _]]的评论中的问题,并将其扩展为make a mutable class:
技术上,是的,您可以扩展它并在其中加入一些var youCanMutateThat : Elem = _
。我猜这不是你想要的。但我想你的计划是允许使用带有可变Col的扩展,我认为你不能这样做,原因如上所述。但是,为什么你首先要有Col [+ _]约束?
答案 1 :(得分:2)
在示例2中,需要为A [_]
添加covarincetrait Bar[A[+_]] extends Foo[A]
因为 Foo 期望协变类型作为参数(+ _是替换类型的约束的一部分),并且继承类型需要保证参数将是协变的(取代类型的限制)。
在示例1中,您定义 Foo (如容器)是参数的协变,并且继承的容器可以是不变的(对替换类型没有限制)
Martin-Löf类型理论的更多细节(预测参数多态性)