在Scala上生活的一个可悲的事实是,如果实例化一个List [Int],则可以验证您的实例是一个List,并且可以验证它的任何单个元素是一个Int,而不是它是一个List [可以很容易地验证:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!
-unchecked选项将责任完全归咎于类型擦除:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
case l : List[String] => println("A list of strings?!")
^
A list of strings?!
为什么会这样,我该如何解决?
此答案使用
Manifest
-API,从Scala 2.10开始不推荐使用。请参阅下面的答案以获取更多最新解决方案。
Scala是用Type Erasure定义的,因为与Java不同,Java虚拟机(JVM)没有泛型。这意味着在运行时,仅存在类,而不存在其类型参数。在该示例中,JVM知道它正在处理a scala.collection.immutable.List
,但不是使用来参数化此列表Int
。
幸运的是,Scala中有一项功能可以让您解决这个问题。这是清单。清单是类,其实例是表示类型的对象。由于这些实例是对象,因此您可以传递它们,存储它们并通常在它们上调用方法。在隐式参数的支持下,它成为一个非常强大的工具。以以下示例为例:
object Registry {
import scala.reflect.Manifest
private var map= Map.empty[Any,(Manifest[_], Any)]
def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
map = map.updated(name, m -> item)
}
def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
map get key flatMap {
case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
}
}
}
scala> Registry.register("a", List(1,2,3))
scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))
scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None
在存储元素时,我们也会存储它的“清单”。清单是一个类,其实例表示Scala类型。这些对象比JVM具有更多信息,这使我们能够测试完整的参数化类型。
但是请注意,aManifest
仍然是一个不断发展的功能。作为其局限性的一个例子,它目前对方差一无所知,并假设一切都是协变的。我希望一旦开发中的Scala反射库完成,它将变得更加稳定和可靠。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句