我有一个协变Scala类型Thing[+B]
。该实现使用内部可变队列:
private val queue : AsyncQueue[B]()
AsyncQueue是一个自定义的可变队列实现,具有特殊属性,我无法在不可变版本中轻松实现。因为它是可变的,所以AsyncQueue是不变的。所以我不能在我的协变类型Thing
中使用它。
由于queue
是私密的,我可以保证我的代码的正确性:例如我不会尝试将queue
分配给Queue[Any]
类型的引用。我如何才能完成这项工作,在Thing
中保持B
协变,而不使用演员表?
(使用强制转换的解决方案是声明AsyncQueue[Object]
并在enqueue / dequeue上强制转换对象,这非常难看。)
ETA :我理解类型协方差,我理解为什么我不能声明协变类型的AsyncQueue,或使AsyncQueue本身协变。我的问题是如何设计此代码以避免在任何地方使用强制转换。
答案 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]
既是协变的又是逆变的,但仅适用于某些成员。