Reputation: 1905
I tried to implement the following function maintaining Scala's invariant view over the JVM arrays:
def cast[T](a: Any): Option[Array[T]] = ???
That is, given:
class Foo
class Bar extends Foo
I want the following examples to return Some
:
val arr1: Any = Array(new Bar)
cast[Bar](arr1) // OK
val arr2: Any = Array(1, 2, 3)
cast[Int](arr2) // OK
val arr3: Any = Array("a", "b", "c")
cast[String](arr3) // OK
For the following one, instead, I want it to return None:
val arr: Any = Array(new Bar)
cast[Foo](arr1) // I want it to be None
val arr2: Any = Array(List(new Bar))
cast[List[Foo]](arr2) // this must be None too!
I tried through reflection using ClassTag/TypeTag
with no luck, but since I'm no reflection expert I could be missing something.
P.S.: I know that having Any
there, is a bad practice but, please, try to see the question academically. You know, just to find out if there's a way to do it and how to.
Update: The solution provided below still does not work for Array(List(new Bar))
because of type erasure.
Upvotes: 2
Views: 916
Reputation: 6237
I think this is what you are after:
import scala.reflect.ClassTag
def cast[T](a: Any)(implicit ct: ClassTag[Array[T]]): Option[Array[T]] = ct.unapply(a)
In the REPL:
scala> val x: Any = Array(1, 2, 3)
x: Any = Array(1, 2, 3)
scala> val y = cast[Int](x)
y: Option[Array[Int]] = Some([I@7a8f55c)
scala> val z = cast[String](x)
z: Option[Array[String]] = None
The solution above doesn't keep invariance; to do it the best way I found so far is this:
def cast[T](in: Any)(implicit ct: ClassTag[Array[T]]): Option[Array[T]] =
if (ct.runtimeClass == in.getClass) ct.unapply(in) else None
Not sure how nice it is but it works:
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Foo
class Bar extends Foo
val x: Any = Array(new Bar)
val y = cast[Foo](x)
val z = cast[Bar](x)
// Exiting paste mode, now interpreting.
defined class Foo
defined class Bar
x: Any = Array(Bar@6109a8cc)
y: Option[Array[Foo]] = None
z: Option[Array[Bar]] = Some([LBar;@7ccebaae)
Upvotes: 2