我尝试在JVM数组上实现以下函数来维护Scala的不变视图:
def cast[T](a: Any): Option[Array[T]] = ???
即给定:
class Foo
class Bar extends Foo
我希望下面的例子返回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
对于下面这个,我希望它返回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!
我尝试通过反射使用ClassTag/TypeTag
没有运气,但由于我不是反射专家,我可能会错过一些东西。
注::我知道有Any
在那里,是一个不好的做法,但是,请尝试看到问题学术。你知道,只是想知道是否有办法做到这一点,以及如何做到。
更新:由于类型擦除,下面提供的解决方案仍然不适用于Array(List(new Bar))
。
我想这就是你想要的:
import scala.reflect.ClassTag
def cast[T](a: Any)(implicit ct: ClassTag[Array[T]]): Option[Array[T]] = ct.unapply(a)
在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
上面的解决方案不保持不变性;到目前为止,我发现最好的方法是:
def cast[T](in: Any)(implicit ct: ClassTag[Array[T]]): Option[Array[T]] =
if (ct.runtimeClass == in.getClass) ct.unapply(in) else None
不知道它有多好,但它工作:
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)