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



我对下面类型检查器的严格性有点困惑 — 似乎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):

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

这真的很难理解(恕我直言),但我相信这意味着要使VariantishT中协变,您必须能够编写

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<:AnimalAnimalInv<:AnimalInv都是真的。

b无效,因为DogInv<:AnimalInv是假的。

c是有效的,因为Dog<:AnimalAnimalInv<:AnimalInv都是真的。

d无效,因为DogInv<:AnimalInv是假的。

因此,正如这些所示TVar不可能是协变的。

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

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

相关内容

  • 没有找到相关文章

最新更新