为什么参数处于逆变位置?

时间:2012-03-08 14:22:59

标签: scala covariance contravariance case-class

我正在尝试在特征中使用协变类型参数来构造像这样的案例类:

trait MyTrait[+T] {
  private case class MyClass(c: T)
}

编译说:

error: covariant type T occurs in contravariant position in type T of value c

然后我尝试了以下但是它也没有用:

trait MyTrait[+T] {
  private case class MyClass[U <: T](c: U)
}

这次错误是:

error: covariant type T occurs in contravariant position in type >: Nothing <: T of type U

有人可以解释为什么T在这里处于协变位置并建议解决这个问题吗? THX!

2 个答案:

答案 0 :(得分:61)

这是面向对象编程的一个基本特征,它没有得到应有的重视。

假设您有一个集合C[+T]+T的含义是:U <: TC[U] <: C[T]。很公平。但是成为子类意味着什么?这意味着每个方法都应该在原始类上工作。所以,假设你有一个方法m(t: T)。这表示您可以使用任何t并对其执行某些操作。但是C[U]只能使用U执行操作,T可能不是全部C[U]!因此,您立即反对C[T]C[T]的子类的说法。这是不是。您可以使用C[U]执行+无法执行的操作。

现在,你怎么解决这个问题?

一个选项是使类不变(删除m[S >: T](s: S))。另一个选择是,如果你采用方法参数,也允许任何超类T。现在,如果U更改为T,则没有什么大问题:U的超类也是{{1}}的超类,该方法可行。 (但是,您必须更改方法才能处理此类事情。)

使用案例类,除非你使其保持不变,否则更难做到正确。我建议这样做,并在其他地方推广泛型和方差。但我需要查看更多详细信息,以确保这对您的用例有用。

答案 1 :(得分:13)

几乎就在那里。这里:

scala> trait MyTrait[+T] {
     |   private case class MyClass[U >: T](c: U)
     | }
defined trait MyTrait

这意味着MyClass[Any]对所有T都有效。这是为什么一个人不能在这个位置使用T的根本原因,但是证明它需要的代码比我现在的情绪要多。 : - )