我有以下课程:
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)
我正在搜索很多关于错误的内容,但找不到有用的内容 帮助我理解错误。
有人可以解释,为什么会发生错误?
答案 0 :(得分:28)
一旦理解,错误信息实际上非常清楚。让我们一起来。
您在类型参数Box
中将类A
声明为协变。这意味着,对于任何类型X
A
X <: A
,Box[X]
,Box[A]
都可以被视为Animal
。
为了举一个明确的例子,我们考虑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]
,您可以将其设为Box
。创建包含两种类型的单个集合并保留def set
类型。
虽然在某些情况下此属性非常方便,但它也会对def set(a:A): Unit
上可用的操作施加限制。这就是编译器不允许您定义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
!方法的参数出现在所谓的&#34;逆变位置&#34;中,这与协方差相反。实际上,如果您定义Box[Cat] >: Box[Animal]
,则Box[Cat]
暗示Box[Animal]
(Box
是Box
的超类型。对于我们的例子,这当然是非感性的。
您的问题的一个解决方案是使case class
类不可变(即不提供任何方式来更改set
的内容),而是使用您的{{中的apply方法定义1}}同伴创建新框。如果需要,您还可以在本地定义Box
,而不是在private[this]
之外的任何位置公开private[this]
,方法是set
。编译器将允许这样做,因为Box
保证我们错误示例的最后一行不会编译,因为A1 Array <
struct <
product array <
struct <
dim1 array < struct <> >
dim2 array < struct <> >
>
>
>>
方法在Lateral view outer explode(A1.product) t1 as prod Lateral view outer explode(prod.dim1) t2 as prod_d1 Lateral view outer explode(prod.dim2) t3 as prod_d2
的特定实例之外是完全不可见的。
答案 1 :(得分:16)
其他人已经给出了为什么代码不能编译的答案,但他们没有给出如何编译代码的解决方案:
> case class Box[+A](v: A) { def set[B >: A](a: B) = Box(a) }
defined class Box
> trait Animal; case class Cat() extends Animal
defined trait Animal
defined class Cat
> Box(Cat()).set(new Animal{})
res4: Box[Animal] = Box($anon$1@6588b715)
> Box[Cat](Cat()).set[Animal](new Animal{})
res5: Box[Animal] = Box($anon$1@1c30cb85)
类型参数B >: A
是一个下限,告诉编译器在必要时推断超类型。正如在示例中看到的那样,在Animal
被给出时推断出Cat
。
答案 2 :(得分:2)
尝试了解Box[+A]
A
中Box[Dog]
协变的含义:
这意味着Box[Animal]
也应该是Box[Dog]
,因此Box[Animal]
的任何实例都应该拥有Box[Dog]
所拥有的所有方法。
特别是,set(a: Animal): Box[Animal]
应该有一个方法
set(a: Dog): Box[Dog]
但是,它只有一个方法
Cat
现在,你认为你可以推断出第二个中的第一个,但事实并非如此:我是否只想使用第二个签名来装箱{{1}}?这是不可行的,这就是编译器告诉你的:方法中的参数是逆变位置(你只能放置逆变(或不变)类型参数)。
答案 3 :(得分:1)
如果A
是协变的,基本上你不能放A
,你只能把它拿出来(例如:返回A
)。如果您希望将A
放入,则需要将其设为contravariant
。
case class Box[-A](value: A)
你想两个都做,然后只做它不变
case class Box[A](value: A)
最好的方法是保持协变性并摆脱制定者并采取不可变的方法。
答案 4 :(得分:0)
除了其他答案外,我还想提供其他方法:
def set[B >: A](x: B): Box[B] = Box(x)