我尝试编译以下代码:
class A[-T]
class B[-T] extends A[A[T]]
我收到以下错误:
error: contravariant type T occurs in covariant position in type [-T]A[A[T]]{def <init>(): B[T]} of class B
为什么这是一个错误?
答案 0 :(得分:4)
这是一个错误,因为{T}中的A[A[T]]
是协变。
来自Wikipedia的协变类型的定义:
在编程语言的类型系统中,键入规则或 类型构造函数是:
- 协变如果它保留了类型(≤)的排序,它将类型从更具体的顺序排列到更通用的类型;
- 逆变,如果它颠倒了这种顺序;
请考虑以下事项:
class Fruit
class Banana extends Fruit
和
Banana <: Fruit
A[Banana] :> A[Fruit] // Because A is contravariant in type T
A[A[Banana]] <: A[A[Fruit]] // Again because A is contravariant in type T
最后一个语句意味着A[A[T]]
是协变,因为它保留了类型的排序,从更具体到更通用。
所以可以这样做:
scala> type AA[+T] = A[A[T]]
defined type alias AA
scala> type AAA[-T] = A[A[A[T]]]
defined type alias AAA
但以下情况会导致错误:
scala> type AA[-T] = A[A[T]]
<console>:15: error: contravariant type T occurs in covariant position in type [-T]A[A[T]] of type AA
type AA[-T] = A[A[T]]
^
scala> type AAA[+T] = A[A[A[T]]]
<console>:15: error: covariant type T occurs in contravariant position in type [+T]A[A[A[T]]] of type AAA
type AAA[+T] = A[A[A[T]]]
^
最后回到原始问题,class B
定义中的方差规则存在相同的违反,因为基类A[A[T]]
在构造T
中是协变的。
为了理解为什么禁止它,我们假设它是可能的:
class B[-T] extends A[A[T]]
在这种情况下,我们得到:
B[Fruit] <: B[Banana] <: A[A[Banana]]
所以
val fruits: B[Fruit] = new B[Fruit]
val bananas1: B[Banana] = fruits
val bananas2: A[A[Banana]] = bananas1
现在,我们的bananas2
类型值A[A[Banana]]
指向A[A[Fruit]]
的{{1}}实例,该实例违反了A[A[Banana]] <: A[A[Fruit]]
以来的类型安全性。
答案 1 :(得分:2)
<init>
是A
(或B
)的构造函数,由编译器定义。
代码的问题是B[-T] extends A[A[T]]
会导致矛盾。
举个例子:
class A[-T]
class B[-T] extends A[A[T]]
class Animal
class Cat extends Animal
简而言之,让我们使用A <: B
表示 A
是B
的子类型。
由于A
和B
对其类型参数T
和Cat <: Animal
都是逆变的,因此
A[Animal] <: A[Cat]
B[Animal] <: B[Cat]
自A[Animal] <: A[Cat]
以来,(再次因为A
的逆转):
A[A[Cat]] <: A[A[Animal]]
另外,根据B
:
B[Cat] = A[A[Cat]]
B[Animal] = A[A[Animal]]
我们已经知道B[Animal] <: B[Cat]
,但如果我们将这些类型转换为上述等值,我们会得到:
B[Animal] <: B[Cat] => A[A[Animal]] <: A[A[Cat]]
但我们已经证明了A[A[Cat]] <: A[A[Animal]]
。我们最终会遇到一种不可能的情况。