如何解决 Scala 上的类型擦除问题?或者,为什么我不能获得我的集合的类型参数? [英] How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?

查看:38
本文介绍了如何解决 Scala 上的类型擦除问题?或者,为什么我不能获得我的集合的类型参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Scala 生活中有一个可悲的事实,如果你实例化一个 List[Int],你可以验证你的实例是一个 List,你可以验证它的任何单个元素是一个 Int,但不是它是一个 List[Int],很容易验证:

It's a sad fact of life on Scala that if you instantiate a List[Int], you can verify that your instance is a List, and you can verify that any individual element of it is an Int, but not that it is a List[Int], as can be easily verified:

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 选项直接将责任归咎于类型擦除:

The -unchecked option puts the blame squarely on type erasure:

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?!

这是为什么,我该如何解决?

Why is that, and how do I get around it?

推荐答案

这个答案使用 Manifest-API,它从 Scala 2.10 开始被弃用.有关更多当前解决方案,请参阅下面的答案.

This answer uses the Manifest-API, which is deprecated as of Scala 2.10. Please see answers below for more current solutions.

Scala 被定义为类型擦除,因为 Java 虚拟机 (JVM) 与 Java 不同,没有泛型.这意味着,在运行时,只存在类,而不存在其类型参数.在这个例子中,JVM 知道它正在处理一个 scala.collection.immutable.List,但不知道这个列表是用 Int 参数化的.

Scala was defined with Type Erasure because the Java Virtual Machine (JVM), unlike Java, did not get generics. This means that, at run time, only the class exists, not its type parameters. In the example, JVM knows it is handling a scala.collection.immutable.List, but not that this list is parameterized with Int.

幸运的是,Scala 中有一项功能可以让您解决这个问题.这是清单.清单是其实例是表示类型的对象的类.由于这些实例是对象,您可以传递它们、存储它们,并且通常对它们调用方法.在隐式参数的支持下,它成为了一个非常强大的工具.以下面的例子为例:

Fortunately, there's a feature in Scala that lets you get around that. It’s the Manifest. A Manifest is class whose instances are objects representing types. Since these instances are objects, you can pass them around, store them, and generally call methods on them. With the support of implicit parameters, it becomes a very powerful tool. Take the following example, for instance:

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

当存储一个元素时,我们存储一个Manifest";它也是.清单是一个类,其实例代表 Scala 类型.这些对象比 JVM 拥有更多信息,这使我们能够测试完整的参数化类型.

When storing an element, we store a "Manifest" of it too. A Manifest is a class whose instances represent Scala types. These objects have more information than JVM does, which enable us to test for the full, parameterized type.

但是请注意,Manifest 仍然是一个不断发展的功能.作为其局限性的一个例子,它目前对方差一无所知,并假设一切都是协变的.我希望在目前正在开发中的 Scala 反射库完成后,它会变得更加稳定和可靠.

Note, however, that a Manifest is still an evolving feature. As an example of its limitations, it presently doesn't know anything about variance, and assumes everything is co-variant. I expect it will get more stable and solid once the Scala reflection library, presently under development, gets finished.

这篇关于如何解决 Scala 上的类型擦除问题?或者,为什么我不能获得我的集合的类型参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆