我对下面类型检查器的严格性有点困惑 — 似乎Inv[T]
的不变T
位置在Variantish
的参数列表中也是不变的:
scala> class Inv[T]
defined class Inv
scala> class Variantish[+T, +TVar <: Inv[T]]
<console>:12: error: covariant type T occurs in invariant position in type <: Inv[T] of type TVar
class Variantish[+T, +TVar <: Inv[T]]
^
变体类型通常可以合法地出现在看起来像不变参数列表的位置,例如具有受对象保护的可见性:
class Variantish[+T](protected[this] var v: Inv[T])
似乎以下内容同样是类型安全的:
class Variantish[+T, +TVar <: Inv[T]](protected[this] var v: TVar)
上面提到的检查需要如此严格吗?
从语言规范(强调我的),关于一致性(即T'
是超类型的T
):
类型构造函数
T
和T′
遵循类似的规则。我们通过类型参数子句[a1,…,an]
和[a′1,…,a′n]
来表征T
和T′
,其中ai
或a′i
可能包括方差注释、高阶类型参数子句和边界。然后,T
符合T′
,如果T′
的有效类型参数的任何列表[t1,…,tn]
(具有声明的方差、边界和高阶类型参数子句)也是T
和T[t1,…,tn]<:T′[t1,…,tn]
的类型参数的有效列表。
这真的很难理解(恕我直言),但我相信这意味着要使Variantish
在T
中协变,您必须能够编写
Variantish[Dog, TVar] <: Variantish[Animal, TVar]
对于任何Variantish[Animal, TVar]
有意义的TVar
。但这甚至对其中一些TVar
没有意义(更不用说有任何真理价值了),例如Inv[Animal]
。这就是为什么在那个地方被禁止的原因。
我不太明白@cyrille-corpet的答案,所以我用一些例子来扩展它。
class Inv[T]
class Variantish[+T, +TVar <: Inv[T]]
trait Animal
class Dog extends Animal
class AnimalInv extends Inv[Animal]
class DogInv extends Inv[Dog]
val a: Variantish[Animal, Inv[Animal]] = new Variantish[Animal, AnimalInv]
val b: Variantish[Animal, Inv[Animal]] = new Variantish[Animal, DogInv]
val c: Variantish[Animal, Inv[Animal]] = new Variantish[Dog, AnimalInv]
val d: Variantish[Animal, Inv[Animal]] = new Variantish[Dog, DogInv]
a
是有效的,因为Animal
<:Animal
和AnimalInv
<:AnimalInv
都是真的。
b
无效,因为DogInv
<:AnimalInv
是假的。
c
是有效的,因为Dog
<:Animal
和AnimalInv
<:AnimalInv
都是真的。
d
无效,因为DogInv
<:AnimalInv
是假的。
因此,正如这些所示TVar
不可能是协变的。
即使在动态类型有效的d
的情况下,它也不是静态类型的子类型。
我怀疑如果我们查看了Variantish
中可能使用TVar
的所有位置,那么它就不需要是类型参数。正如@concat指出的那样,您可能出现的任何差异错误都可以通过使用受对象保护的访问修饰符来解决。