在多个类型的特征实例中键入方差

时间:2014-12-10 08:36:36

标签: scala generics multiple-inheritance covariance traits

考虑

trait Foo[+A, +B]

trait Foo1[+A] extends Foo[A, Nothing]

trait Foo2[+B] extends Foo[Nothing, B]

new Foo1[String] with Foo2[Nothing]有效。

new Foo1[Nothing] with Foo2[String]有效。

new Foo1[String] with Foo2[String]没有:

error: illegal inheritance;
<$anon: Foo1[String] with Foo2[String]> inherits different type instances of trait Foo:
Foo[Nothing,String] and Foo[String,Nothing]
          new Foo1[String] with Foo2[String]
              ^

在第一种情况下,实例似乎是Foo[String, Nothing]的子类型。

同样,在第二种情况下,实例似乎应该是Foo[String, String]的子类型。

这两者之间有什么区别只会导致一个编译?

1 个答案:

答案 0 :(得分:8)

如果您看到编译器实际上解释得很好:

: Foo1[String] with Foo2[String]> inherits different type instances of trait Foo:
Foo[Nothing,String] and Foo[String,Nothing]
              new Foo1[String] with Foo2[String]

首先解释一下:

new Foo1[String] with Foo2[Nothing]

看看这个例子:

scala> val x = new Foo1[String] with Foo2[Nothing]
x: Foo1[String] with Foo2[Nothing] = $anon$1@58651fd0

scala> val y:Foo[String, Nothing] = x
y: Foo[String,Nothing] = $anon$1@58651fd0

根据规范,多个with特征的实例化从左到右发生。所以首先Foo1被实例化。执行new Foo1[String]会为您提供Foo[String, Nothing]with Foo2[Nothing]为您提供Foo[Nothing, Nothing]。现在Foo是其第一个类型参数的共变体。简而言之,这是有效的:

scala> val a = new Foo[Nothing, Nothing]{}
a: Foo[Nothing,Nothing] = $anon$1@134593bf

scala> val b:Foo[String, Nothing] = a
b: Foo[String,Nothing] = $anon$1@134593bf

因此你可以使用Foo [String,Nothing]而不是Foo [Nothing,Nothing]。这使您能够实例化y

如果:

new Foo1[String] with Foo2[String]

new Foo1[String]给出Foo[String, Nothing]with Foo2[String]给出Foo[Nothing, String]。而且它们都是矛盾的(由于它的第二个参数:

with Foo2[String](Foo [Nothing,String])变为Foo [String,String]。

scala> val p :Foo[String, String] = new Foo[Nothing, String]{}
p: Foo[String,String] = $anon$1@39529185

Foo [String,String] 不能成为Foo [String,Nothing] 。 (val p :Foo[String, Nothing] = new Foo[Nothing, String]{}失败)

因此错误。


<强>在变 如果Foo在其第一个参数上不是变体,那么它不起作用:

scala> trait Foo[A, +B]
defined trait Foo

scala> trait Foo1[A] extends Foo[A, Nothing]
defined trait Foo1

scala> trait Foo2[+B] extends Foo[Nothing, B]
defined trait Foo2

scala> new Foo1[String] with Foo2[Nothing]
<console>:11: error: illegal inheritance;
 <$anon: Foo1[String] with Foo2[Nothing]> inherits different type instances of trait Foo:
Foo[Nothing,Nothing] and Foo[String,Nothing]
              new Foo1[String] with Foo2[Nothing]

<强>禁忌方差 如果Foo在其第二种类型上具有反变量,那么两种语句都可以工作

scala> trait Foo[+A, -B]
defined trait Foo

scala> trait Foo1[+A] extends Foo[A, Nothing]
defined trait Foo1

scala> trait Foo2[-B] extends Foo[Nothing, B]
defined trait Foo2

scala> new Foo1[String] with Foo2[String]
res0: Foo1[String] with Foo2[String] = $anon$1@77468bd9

scala> new Foo1[String] with Foo2[Nothing]
res1: Foo1[String] with Foo2[Nothing] = $anon$1@51016012

它只是起作用,因为现在: Foo [String,String] 可以变成Foo [String,Nothing]