4

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

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

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

trait Covariant[+A] {
  def problematic[B <: A](x : B)
}

产生错误信息

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

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

trait Contravariant[-A] {
  def problematic[B >: A](x : B)
}

产生错误信息

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

正如我所提到的,很容易从形式上看出(即遵循方差注释的规则)为什么会发生这些错误。但是,我想不出一个例子来说明这些限制的必要性。相比之下,很容易想出一些例子来说明为什么方法参数应该改变方差位置,参见例如Checking Variance Annotations

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

请注意,Scala type bounds & variance的答案让这个特定的问题悬而未决,而“下界”中给出的答案将反转类型的方差,但为什么呢?对我来说似乎错了。

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

trait Queue[+T] {
  def head : T
  def tail :  Queue[T]
  def enqueue[U <: T](x : U) : Queue[T]
}

然后我们可以实现

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)
  }
}

并将其用作

val x : Queue[Any] = new StrangeIntQueue
x.enqueue("abc")

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

4

2 回答 2

1

使用++from 方法List查看为什么需要限制。请注意,这需要++产生一个List[B]

 def ++[B](that: GenTraversableOnce[B]): List[B] 

带有完整的签名

 def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That

那么为什么重要[B >: A]。好吧,如果我们想组合这样的东西怎么办

 trait Foo
 trait Bar extends Foo

我们有一个有签名的方法

 def op(that: List[Foo], other: Foo): List[Foo] = that ++ List(other)

我可以将类型列表传递给它,Bar但为了能够将它作为一个类型返回,List[Foo]我必须做出这样的条件,Foo >: Bar以便我实际上可以执行以下操作

 def see(that: List[Bar]): List[Foo] = op(that, myFoo)

这本质上是在做 aList[Bar] ++ List[Foo]以返回通过类型List[Foo]表达的List[Foo]类型。这就是翻转发生的原因。

现在,如果我试图强制执行,Foo <: Bar我将立即遇到List[Bar] ++ List[Foo]无法返回类型列表的问题Foo(更不用说它与上面的定义冲突。)它只能返回 aList的最小上限。

于 2014-05-30T16:32:41.283 回答
1

假设我们允许一个类有一个类型参数[-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!!!!!!

最后一行会输入检查,cDog problem new Dog实际上会返回一个Mammal. 这显然不是一件好事。谢天谢地,类型系统实际上并没有让我们这样做。

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

我希望这个例子有帮助。

于 2014-05-30T19:31:17.050 回答