3

我有一个协变的 Scala 类型Thing[+B]。该实现使用内部可变队列:

private val queue : AsyncQueue[B]()

AsyncQueue 是一个自定义的可变队列实现,具有我无法在不可变版本中轻松实现的特殊属性。因为它是可变的,所以 AsyncQueue 是不变的。所以我不能在我的协变类型中使用它Thing

由于queue是私有的,我可以保证我的代码的正确性:例如,我不会尝试分配queue给 type 的引用Queue[Any]。如何在不使用强制转换的情况下使这项工作保持Thing协变?B

(使用 cast 的解决方案是在 enqueue/dequeue 上声明一个AsyncQueue[Object]和 cast 对象,这非常难看。)

ETA:我了解类型协变,并且我了解为什么我不能声明协变类型的 AsyncQueue 或使 AsyncQueue 本身协变。我的问题是如何设计此代码以避免在任何地方使用强制转换。

4

3 回答 3

4

private[this]根据规范,您可以通过使您的成员免受差异检查的影响。

scala> trait Thing[+A] { def next(): A }
defined trait Thing

预计,

scala> class Thingie[+A](implicit t: ClassTag[A]) extends Thing[A] { val as = mutable.ArrayBuffer.fill[A](10)(t.runtimeClass.newInstance.asInstanceOf[A]) ; private val it = as.iterator ; def next() = it.next() }
<console>:12: error: covariant type A occurs in invariant position in type => scala.collection.mutable.ArrayBuffer[A] of value as
       class Thingie[+A](implicit t: ClassTag[A]) extends Thing[A] { val as = mutable.ArrayBuffer.fill[A](10)(t.runtimeClass.newInstance.asInstanceOf[A]) ; private val it = as.iterator ; def next() = it.next() }

scala> class Thingie[+A](implicit t: ClassTag[A]) extends Thing[A] { private[this] val as = mutable.ArrayBuffer.fill[A](10)(t.runtimeClass.newInstance.asInstanceOf[A]) ; private val it = as.iterator ; def next() = it.next() }
defined class Thingie

scala> class X
defined class X

scala> val xs = new Thingie[X]
xs: Thingie[X] = Thingie@49f5c307

scala> xs.next
res1: X = X@4816c290
于 2014-05-18T05:21:07.883 回答
3

你需要@uncheckedVariance

import scala.annotation.unchecked.uncheckedVariance

class A[T] {}
class B[+T] {
  val a: A[T @uncheckedVariance] = null
}

甚至 Scala 标准库也使用@uncheckedVariance,特别是允许不变的可变集合从协变特征继承。

于 2014-05-18T03:07:46.503 回答
1

如果B在 中是协变的,Thing[+B]那么您将无法B在 中处于逆变位置Thing,即

def put(b:B) {...} // will fail to compile, can't use a covariant type in this position

但是可以为 建立两个接口Thing,一个用于协变位置,一个用于逆变位置,如下所示:

trait ThingProduce[+B] {
  def get: B 
}
trait ThingConsume[-B] {
  def put(b: B)
}

class Thing[B] extends ThingConsume[B] with ThingProduce[B] {
  private val queue = new scala.collection.mutable.Queue[B]

  def put(b: B) {queue.enqueue(b)}
  def get: B = queue.dequeue

  def both(b: B): B = ???
}

这样与类层次结构:

class Animal
class Mammal extends Animal
class Dog extends Mammal

可以执行以下操作:

val mammalThing: Thing[Mammal] = new Thing[Mammal]{}
val dogConsumer: ThingConsume[Dog] = mammalThing
val animalProducer: ThingProduce[Animal] = mammalThing

但不是:

val dogConsumer: ThingConsume[Dog] = animalProducer
//or
val animalProducer: ThingProduce[Animal] = dogConsumer

因此Thing[B]可以看出既是协变的又是逆变的,但仅适用于某些成员。

于 2014-05-18T00:52:26.803 回答