我有以下类:
case class Box[+A](value: A) {
def set(a: A): Box[A] = Box(a)
}
编译器抱怨:
Error:(4, 11) covariant type A occurs in contravariant position in type A of value a
def set(a: A): Box[A] = Box(a)
我搜索了很多关于这个错误的信息,但是找不到有用的东西来帮助我理解这个错误。
有人能解释一下为什么会出现错误吗?
5条答案
按热度按时间wpx232ag1#
一旦你理解了错误信息,它实际上是非常清楚的。让我们一起来看看。
你在类
A
的类型参数A
中声明类Box
为协变量,这意味着对于扩展A
的任何类型X
(即X <: A
),Box[X]
都可以看作是Box[A]
。为了给予一个清晰的例子,让我们考虑
Animal
类型:如果你定义了
Dog <: Animal
和Cat <: Animal
,那么Box[Dog]
和Box[Cat]
都可以看作Box[Animal]
,你可以创建一个包含这两种类型的集合,并保留Box[Animal]
类型。虽然这个属性在某些情况下非常方便,但是它也对
Box
上可用的操作施加了约束,这就是编译器不允许定义def set
的原因。如果你允许定义
则以下代码有效:
最后一行显然是个问题,因为
catBox
现在将包含一个Dog
!方法的参数出现在所谓的“逆变位置”,这与协方差相反。实际上,如果你定义Box[-A]
,那么Cat <: Animal
就意味着Box[Cat] >: Box[Animal]
(Box[Cat]
是Box[Animal]
的超类型)。对于我们的例子,这当然是没有意义的。您的问题的一个解决方案是使
Box
类不可变(例如,不提供任何方式来更改Box
的内容),而是使用在您的case class
伙伴中定义的apply方法来创建新框。您还可以在本地定义set
,并且通过将其声明为private[this]
而不在Box
之外的任何地方公开它。编译器将允许这样做,因为private[this]
保证了错误示例的最后一行不会被编译,因为set
方法在Box
的特定示例之外是完全不可见的。如果由于某种原因,您不想使用apply方法创建新示例,也可以按如下方式定义
set
。muk1a3rh2#
其他人已经给出了为什么代码不能编译的答案,但是他们还没有给出如何使代码编译的解决方案:
类型参数
B >: A
是一个下界,它告诉编译器在必要时推断出一个超类型,正如在示例中可以看到的,当给定Cat
时,Animal
被推断出。cvxl0en23#
试着理解你的
Box[+A]
在A
中协变意味着什么:这意味着
Box[Dog]
也应该是Box[Animal]
,因此Box[Dog]
的任何示例都应该具有Box[Animal]
具有的所有方法。特别是,
Box[Dog]
应该具有方法但是,它只有一个方法
现在,你可能认为你可以从第二个推论出第一个,但事实并非如此:你想只使用第二个签名来装箱一个
Cat
吗?这是不可行的,这是编译器告诉你的:方法中的参数是一个逆变位置(你只能放置逆变(或不变)类型参数)。2fjabf4q4#
除了其他答案外,我还想提供另一种方法:
vkc1a9a25#
基本上,如果
A
是协变的,则不能将A
放入,只能将其取出(例如:返回A
)。如果希望放入A
s,则需要将其设置为contravariant
。你想两个都做吗,那就让它不变
最好的方法是保持它的协变性,去掉setter,采用一种不变的方法。