为什么Scala中的类型参数列表中的所有不变泛型类位置都不变?

时间:2017-04-06 15:48:49

标签: scala generics invariance

我对下面的类型检查器的严格性感到有点困惑 - 似乎T的{​​{1}}位置在Inv[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]]
                             ^

似乎以下内容与typeafe一样:

class Variantish[+T](protected[this] var v: Inv[T])

上面提到的检查是否必须如此严格?

2 个答案:

答案 0 :(得分:1)

从语言specification(强调我的),关于一致性(即T'T的超类型:

  

类型构造函数TT′遵循类似的规则。我们通过类型参数子句TT′来表征[a1,…,an][a′1,…,a′n],其中aia′i可能包含方差注释,高阶类型参数子句和边界。然后,T符合T′ if 任何列表[t1,…,tn] - 具有声明的差异,边界和高阶类型参数子句 - 有效类型参数T′也是TT[t1,…,tn]<:T′[t1,…,tn]的有效参数列表。

这真的很难理解(恕我直言),但我认为这意味着Variantish中的T是协变的,你必须能够写

Variantish[Dog, TVar] <: Variantish[Animal, TVar]

任何 TVar Variantish[Animal, TVar]有意义。但对于TVar中的某些人来说,这甚至没有意义(更不用说有任何真值),例如Inv[Animal]。这就是为什么在那个地方被禁止的原因。

答案 1 :(得分:0)

我不太明白@ 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&lt;:AnimalAnimalInv&lt;:AnimalInv都是正确的。

b无效,因为DogInv&lt;:AnimalInv为false。

c有效,因为Dog&lt;:AnimalAnimalInv&lt;:AnimalInv都是正确的。

d无效,因为DogInv&lt;:AnimalInv为false。

因为这些节目TVar不能协变。

即使在动态类型有效的d的情况下,它也不是静态类型的子类型。

我怀疑如果我们在TVar中查看了我们可能使用Variantish的所有位置,那么它不需要是类型参数。正如@concat所指出的那样,您可以通过使用受对象保护的访问修饰符来解决任何方差错误。