我正在尝试在特征中使用协变类型参数来构造像这样的案例类:
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!
答案 0 :(得分:61)
这是面向对象编程的一个基本特征,它没有得到应有的重视。
假设您有一个集合C[+T]
。 +T
的含义是:U <: T
,C[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
的根本原因,但是证明它需要的代码比我现在的情绪要多。 : - )