协变类型 A 出现在值 a 的类型 A 中的逆变位置 [英] covariant type A occurs in contravariant position in type A of value a
问题描述
我有以下课程:
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)
我搜索了很多关于错误的信息,但找不到有用的东西帮助我理解错误.
I was searching a lot about the error, but could not find something useful that help me to understand the error.
谁能解释一下,为什么会发生错误?
Could someone please explain, why the error occurs?
推荐答案
错误信息其实一看就明白了.让我们一起去那里.
The error message is actually very clear once you understand it. Let's get there together.
您在其类型参数 A
中将 Box
类声明为协变类.这意味着对于任何类型的 X
扩展 A
(即 X <: A
),Box[X]
可以看作是一个Box[A]
.
You are declaring class Box
as covariant in its type parameter A
. This means that for any type X
extending A
(i.e. X <: A
), Box[X]
can be seen as a Box[A]
.
为了给出一个清晰的例子,让我们考虑 Animal
类型:
To give a clear example, let's consider the Animal
type:
sealed abstract class Animal
case class Cat extends Animal
case class Dog extends Animal
如果你定义了 Dog <: Animal
和 Cat <: Animal
,那么 Box[Dog]
和 Box[Cat]
可以被视为 Box[Animal]
并且你可以例如创建一个包含这两种类型的集合并保留 Box[Animal]
类型.
If you define Dog <: Animal
and Cat <: Animal
, then both Box[Dog]
and Box[Cat]
can be seen as Box[Animal]
and you can e.g. create a single collection containing both types and preserve the Box[Animal]
type.
尽管此属性在某些情况下非常方便,但它也对您可以在 Box
上提供的操作施加了限制.这就是编译器不允许您定义 def set
的原因.
Although this property can be very handy in some cases, it also imposes constraints on the operations you can make available on Box
. This is why the compiler doesn't allow you to define def set
.
如果你允许定义
def set(a:A): Unit
那么下面的代码是有效的:
then the following code is valid:
val catBox = new Box[Cat]
val animalBox: Box[Animal] = catBox // valid because `Cat <: Animal`
val dog = new Dog
animalBox.set(dog) // This is non-sensical!
最后一行显然有问题,因为 catBox
现在将包含一个 Dog
!方法的参数出现在所谓的逆变位置"中,这与协方差相反.事实上,如果你定义了 Box[-A]
,那么 Cat <: Animal
意味着 Box[Cat] >: Box[Animal]
(Box[Cat]
是 Box[Animal]
的超类型).对于我们的示例,这当然是无意义的.
The last line is obviously a problem because catBox
will now contain a Dog
! The arguments of a method appear in what is called "contravariant position", which is the opposite of covariance. Indeed, if you define Box[-A]
, then Cat <: Animal
implies Box[Cat] >: Box[Animal]
(Box[Cat]
is a supertype of Box[Animal]
). For our example, this is of course non-sensical.
解决您的问题的一种方法是使 Box
类不可变(即不提供任何方法来更改 Box
的内容),而是使用 apply在您的 case class
伴侣中定义的方法来创建新框.如果需要,您也可以在本地定义 set
并且不会通过将其声明为 private[this]
来将其暴露在 Box
之外的任何地方.编译器将允许这样做,因为 private[this]
保证我们错误示例的最后一行不会编译,因为 set
方法在特定实例之外是完全不可见的盒子
.
One solution to your problem is to make the Box
class immutable (i.e. to not provide any way to change the content of a Box
), and instead use the apply method defined in your case class
companion to create new boxes. If you need to, you can also define set
locally and not expose it anywhere outside Box
by declaring it as private[this]
. The compiler will allow this because the private[this]
guarantees that the last line of our faulty example will not compile since the set
method is completely invisible outside of a specific instance of Box
.
如果由于某种原因您不想使用 apply 方法创建新实例,您也可以如下定义 set
.
If for some reason you do not want to create new instances using the apply method, you can also define set
as follows.
def set[B >: A](b: B): Box[B] = Box(b)
这篇关于协变类型 A 出现在值 a 的类型 A 中的逆变位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!