协变类型 A 出现在值 a 的类型 A 中的逆变位置 [英] covariant type A occurs in contravariant position in type A of value a

查看:41
本文介绍了协变类型 A 出现在值 a 的类型 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 <: AnimalCat <: 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆