使用协变类型的私有可变集合

时间:2014-05-17 16:45:40

标签: scala covariant

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

private val queue : AsyncQueue[B]()

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

由于queue是私密的,我可以保证我的代码的正确性:例如我不会尝试将queue分配给Queue[Any]类型的引用。我如何才能完成这项工作,在Thing中保持B协变,而不使用演员表?

(使用强制转换的解决方案是声明AsyncQueue[Object]并在enqueue / dequeue上强制转换对象,这非常难看。)

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

3 个答案:

答案 0 :(得分:4)

根据the spec,您可以根据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

答案 1 :(得分:3)

您需要@uncheckedVariance

import scala.annotation.unchecked.uncheckedVariance

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

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

答案 2 :(得分: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]既是协变的又是逆变的,但仅适用于某些成员。

相关问题