I have a covariant Scala type Thing[+B]
. The implementation uses an internal mutable queue:
private val queue : AsyncQueue[B]()
AsyncQueue is a custom mutable queue implementation, with special properties which I can't easily implement in an immutable version. Because it's mutable, AsyncQueue is invariant. So I can't use it in my covariant type Thing
.
Since queue
is private, I can guarantee the correctness of my code: e.g. I won't try to assign queue
to a reference of type Queue[Any]
. How can I make this work, keeping Thing
covariant in B
, without using casts?
(The solution with casts is to declare an AsyncQueue[Object]
and cast objects on enqueue/dequeue, which is very ugly.)
ETA: I understand type covariance, and I understand why I shouldn't be able to declare an AsyncQueue of a covariant type, or make AsyncQueue itself covariant. My question is how to design this code to avoid using casts everywhere.
You can make your member immune to the variance check by making it private[this]
, according to the spec.
scala> trait Thing[+A] { def next(): A }
defined trait Thing
expectedly,
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() }
but
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
and
scala> class X
defined class X
scala> val xs = new Thingie[X]
xs: Thingie[X] = Thingie@49f5c307
scala> xs.next
res1: X = X@4816c290