A toArray function that breaks on primitive type, but works fine when written as a PartialFunction?

79 Views Asked by At

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?

0

There are 0 best solutions below