为什么类型下限会改变方差位置?

god*_*lka 5 scala generic-variance

Scala语言规范(关于方差注释的第 4.5 节,第 44 页)

  • 类型参数的方差位置与封闭类型参数子句的方差位置相反。
  • 类型声明或类型参数下界的方差位置与类型声明或参数的方差位置相反。

使用上面的第一点,很容易看出(至少在形式上)

trait Covariant[+A] {
  def problematic[B <: A](x : B)
}
Run Code Online (Sandbox Code Playgroud)

产生错误消息

error: covariant type A occurs in contravariant position in type >: Nothing <: A of type B
       def problematic[B <: A](x : B)
Run Code Online (Sandbox Code Playgroud)

使用第一点和第二点很容易看出

trait Contravariant[-A] {
  def problematic[B >: A](x : B)
}
Run Code Online (Sandbox Code Playgroud)

产生错误消息

error: contravariant type A occurs in covariant position in type >: A <: Any of type B
             def problematic[B >: A](x : B)
Run Code Online (Sandbox Code Playgroud)

正如我所提到的,很容易从形式上(即遵循方差注释的规则)看出这些错误发生的原因。然而,我无法举出一个例子来说明这些限制的必要性。相反,很容易举出示例来说明为什么方法参数应该更改方差位置,请参见检查方差注释

所以,我的问题是:假设上面的两段代码是允许的,那么会出现哪些问题的例子?这意味着,我正在寻找与此类似的示例,以说明如果不使用上述两条规则,可能会出现什么问题。我对涉及下限类型的示例特别感兴趣。

请注意, Scala 类型边界和方差的答案使这个特定问题悬而未决,而“下限”中给出的答案将反转类型的方差,但为什么呢?我觉得不对。

编辑:我认为第一种情况可以按如下方式处理(改编上面引用的示例)。假设以下内容是允许的

trait Queue[+T] {
  def head : T
  def tail :  Queue[T]
  def enqueue[U <: T](x : U) : Queue[T]
}
Run Code Online (Sandbox Code Playgroud)

然后我们就可以实现

class QueueImplementation[+T] extends Queue[T] {
  /* ... implement Queue here ... */
}

class StrangeIntQueue extends QueueImplementation[Int] {
  override def enqueue[U <: Int](x : U) : Queue[Int] = {
    println(math.sqrt(x))
    super.enqueue(x)
  }
}
Run Code Online (Sandbox Code Playgroud)

并将其用作

val x : Queue[Any] = new StrangeIntQueue
x.enqueue("abc")
Run Code Online (Sandbox Code Playgroud)

这显然很麻烦。但是,我看不出如何对此进行调整以表明“逆变类型参数+下类型界限”的组合也是有问题的?

ggo*_*van 2

假设我们允许一个类有一个类型参数[-T],并且该类上的一个方法有[U >: T]......

for come class hierarchy
Dog <: Mammal <: Animal

class Contra[-X](x: X){
  def problem[Y >: X](y: Y): Y = x // X<:Y so this would be valid
}

val cMammal:Contra[Mammal] = new Contra(new Mammal)

val a:Animal = cMammal problem new Animal // Animal >: Mammal, this is fine
val m:Mammal = cMammal problem new Mammal // Mammal >: Mammal, this is fine
val d:Mammal = cMammal problem new Dog    // (Dog upcasts to Mammal) >: Mammal, this is fine

val cDog:Contra[Dog] = cMammal // Valid assignment

val a:Animal = cDog problem new Animal // Animal >: Mammal, this is fine
val m:Mammal = cDog problem new Mammal // Mammal >: Mammal, this is fine
val d:Dog    = cDog problem new Dog    // AAAHHHHHHH!!!!!!
Run Code Online (Sandbox Code Playgroud)

最后一行将类型检查,cDog problem new Dog实际上会返回一个Mammal. 这显然不是什么好事。值得庆幸的是,类型系统实际上并不让我们这样做。

QED 逆变类型参数 + 类型下限混合不是一个好主意。

我希望这个例子有帮助。