2

Playing around with a reflection-base code I've come a cross a situation where I want to convert a sequence into array. The catch is that the only type information available is in the form of a runtime.universe.Type, and the sequence itself is of type Seq[Any].

I attempted to call the toArray method on the Seq, which requires a ClassTag that I don't have. Naively I just created a ClassTag[Any] out of the runtime Type, like this:

import scala.reflect.runtime.{universe => ru}
import scala.reflect.ClassTag

val mirror = ru.runtimeMirror(getClass.getClassLoader)

def anyClassTag(tpe: ru.Type) = ClassTag[Any](mirror.runtimeClass(tpe))

def toArray1(items: Seq[Any]) = (tpe: ru.Type) => {
  items.toArray(anyClassTag(tpe))
}

Unsurpisingly this fails with a CastClassException when called with a primitive type.

toArray1(Seq(1,2,3))(ru.typeOf[Int]) foreach print // java.lang.ClassCastException: [I cannot be cast to [Ljava.lang.Object;

However, if the function is rewritten as a PartialFunction instead, then it somehow works!

def toArray2(items: Seq[Any]) : PartialFunction[ru.Type, Array[Any]] = { case tpe =>
  items.toArray(anyClassTag(tpe))
}

toArray2(Seq(1,2,3))(ru.typeOf[Int]) foreach print // Successfully prints 123 !

This seems to be very fragile though. A slight modification could easily break it again. Example:

def toArray3(items: Seq[Any]) : PartialFunction[ru.Type, Array[Any]] = { case tpe =>
  val results = items.toArray(anyClassTag(tpe))
  results
}

toArray3(Seq(1,2,3))(ru.typeOf[Int]) foreach print // ClassCastException again!

So the question is, what magic is going on here? Why is toArray2 the only one that work, and should it?

Worakarn Isaratham
  • 1,034
  • 1
  • 9
  • 16
  • Runtime reflection in Scala is slow and has a number severe of bugs, including regressions. Please check your Scala version with a list of opened or recently closed bugs: https://github.com/scala/bug/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Areflection+ – Andriy Plokhotnyuk Nov 17 '19 at 10:56

0 Answers0