I was recently writing some Scala code, and I wanted to return an array of generic type:

class Foo[+T] {
  def someArray(size: Int): Array[T] = {
    val result = new Array[T](size)
    ...
    result
  }
}

This did not make Scala happy:

[info] Compiling 1 Scala source to /home/stucchio/src/breeze/math/target/scala-2.11/classes...
[error] /home/stucchio/src/breeze/math/src/main/scala/breeze/stats/distributions/Rand.scala:66: cannot find class tag for element type T
[error]     val result = new Array[T](size)

The standard solution to this is simply to pass an implicit classtag argument:

class Foo[+T] {
  def someArray(size: Int)(implicit m: ClassTag[T]): Array[T] = {
    val result = new Array[T](size)
    ...
    result
  }
}

Unfortunately, due to the +T in the class definition, that won't work:

[info] Compiling 1 Scala source to /home/stucchio/src/breeze/math/target/scala-2.11/classes...
[error] /home/stucchio/src/breeze/math/src/main/scala/breeze/stats/distributions/Rand.scala:65: covariant type T occurs in invariant position in type (size: Int)(implicit m: scala.reflect.ClassTag[T])Array[T] of method someArray

The problem here is the covariance - the Array[T] class is invariant in T. If we were allowed to do this, the following could happen:

val intFoo = new Foo[Int]
val anyFoo : Foo[Any] = intFoo

val anyArr : Array[Any] = anyFoo.someArray(1024)

anyArr(0) = intFoo //Throws runtime exception, since anyArr is (under the hood) an Array[Int]

The solution I found was to add an additional type parameter to the someArray method:

class Foo[+T] {
  def someArray[U >: T](size: Int)(implicit m: ClassTag[U]): Array[U] = {
    val result = new Array[U](size)
    ...
    result
  }
}

The net result:

> (new Foo[Int]).someArray(1024)
Array[Int] = Array(0,1,2,3, ...)
> val r: Foo[Any] = new Foo[Int]
> r.someArray(1024)
Array[Any] = Array(0,1,2,3, ...)

This is of course the desired behavior.


Subscribe to the mailing list


Comments

comments powered by Disqus