斯卡拉:'协变型R出现在不变位置'的原因是什么?

时间:2016-06-05 21:06:12

标签: scala generics covariance contravariance

给出以下示例代码:

trait Gen[T, +R] {

  def follow[K](gen: Gen[R, K]): Gen[T, K]

  def i: T
  def j: R
}
case class GenImpl[T, R](i: T, j: R) extends Gen[T, R] {

  override def follow[K](gen: Gen[R, K]): Gen[T, K] = {
    GenImpl[T, K](this.i, gen.j)
  }
}

编译器将给出以下错误:

Error:(9, 17) covariant type R occurs in invariant position in type Gen[R,K] of value gen
  def follow[K](gen: Gen[R, K]): Gen[T, K]
                ^

但是,此代码在运行时可能无法通过类型验证失败。 R的协变仅仅意味着:

R1 <: R, R2 <: R

然后:

Gen[T, R1] <:< Gen[T, R]

因此Gen [R,K]可以是Gen [T,R]和Gen [T,R1]的.follow()的参数。但是Gen [R1,K]只能是Gen [T,R1]的.follow()的参数,如果应用于Gen [T,R2]或Gen [T,R],它将触发编译错误。没有必要将Gen [R / R1,K]中的R或R1设置成逆变来完成它的工作。

我看不到可以通过编译并在运行时失败的情况。你怎么看?编译器是否会引发误报?

1 个答案:

答案 0 :(得分:2)

假设我们有

class R0
class R1 extends R0
class T0

然后Gen[T0, R0]的任何实例都必须提供以下服务:

def follow[K](gen : Gen[R0, K]) : Gen[T0,K]

方差注释声称Gen[T0, R1] <:< Gen[T0, R0]。所以它必须是可替代的并提供相同的服务。但是Gen[T0, R1]实际上提供了以下服务:

def follow[K](gen: Gen[R1, K]) : Gen[T0,K]

假设我有这样的代码:

def doFollow[K]( g : Gen[T0, R0], h : Gen[R0, K] ) : Gen[T0,K] = {
  g.follow( h )
}

冷却。让我们假设我有一些实例,但是:

val g0 : Gen[T0, R0]     = ???
val h0 : Gen[R0, String] = ???
val g1 : Gen[T0, R1]     = ???

我打电话给doFollow[String]( g0 , h0 ),一切都很好,我得到了一个结果。

我在方差注释中声明的{p> g1可以替代g0。所以现在我尝试doFollow[String]( g1 , h0 )。哎呀。然后我必须在函数体中执行

g1.follow[String]( h0 )

g1只知道如何关注Gen[R1, String]h0Gen[R0, String]。由于第一个参数被声明为不变(它必须是逆变的),所以Gen[R0, String] <:< Gen[R1, String] 不是真的。因此h0不是g1关注方法的可接受参数,而g0跟随方法是可接受的参数。 g1类型Gen[T0, R1]的内容实际上不能替代g0类型Gen[T0, R0]的内容。

但您的方差注释要求Gen[T0, R1]对象可以替代Gen[T0, R0] s。他们不能,编译器正确地打电话给你。