32

我正在尝试在特征中使用协变类型参数来构造一个案例类,如下所示:

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 在这里处于协变位置并为这个问题提出解决方案吗?谢谢!

4

2 回答 2

61

这是面向对象编程的一个基本特征,并没有得到应有的关注。

假设您有一个集合C[+T]。什么+T意思是如果U <: T,那么C[U] <: C[T]。很公平。但是成为子类意味着什么?这意味着每个方法都应该适用于原始类。所以,假设你有一个方法m(t: T)。这表示你可以拿走任何t东西并用它做点什么。但C[U]只能做事用U,这未必是全部的T!因此,您立即反驳了您的主张,C[U]C[T]. 不是。_ 有些事情你可以用 a 做C[T]而你不能用 a 做C[U]

现在,你如何解决这个问题?

一种选择是使类不变(删除+)。另一种选择是,如果您采用方法参数,则也允许任何超类m[S >: T](s: S). 现在,如果T更改为U,这没什么大不了的: 的超类T也是 的超类U,并且该方法将起作用。(然而,你必须改变你的方法才能处理这些事情。)

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

于 2012-03-08T15:15:26.407 回答
13

差不多好了。这里:

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

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

于 2012-03-08T15:33:25.857 回答