Scala是否可以在不更改JVM的情况下使用具体化的generics?

我最近开始学习Scala并且很失望(但并不感到惊讶)他们的generics也通过类型擦除来实现。

我的问题是,Scala是否有可能使用具体的generics,或者JVM是否需要以某种方式进行更改? 如果确实需要更改JVM,究竟需要更改什么?

否 – 如果该字节码不支持具体化的generics,则Scala不可能作为Java等效字节码运行。

当你问“需要改变什么?” ,答案是: 字节码规范 。 目前,字节码不允许定义变量的参数化类型。 已经决定,由于对字节码的修改以支持具体化的generics将破坏向后兼容性 , 因此必须通过类型擦除来实现generics 。

为了解决这个问题,Scala利用其implicit机制的强大function来定义一个Manifest ,它可以在任何范围内导入,以便在运行时发现类型信息。 清单是实验性的,很大程度上没有文档,但它们将作为2.8中的库的一部分出现 。 这是Scala reified generics / Manifests的另一个很好的资源

为了补充oxbow_lakes,Stack Overflow上有一个关于如何在Scala中绕过类型擦除的问题 。

“隐式Manifest”是一个Scala编译器技巧,它不会使Scala中的generics具体化。 Scala编译器,当它看到一个带有“隐式m:Manifest [A]”参数的函数并且在调用站点知道A的generics类型时 ,它会将A类及其generics类型参数包装到Manifest中并生成它可以在函数内部使用。 但是,如果它无法找出A的真实类型,那么就无法创建Manifest。 换句话说,如果内部函数需要Manifest,则必须传递函数调用链。

 scala> def typeName[A](a: A)(implicit m: reflect.Manifest[A]) = m.toString typeName: [A](a: A)(implicit m: scala.reflect.Manifest[A])java.lang.String scala> typeName(List(1)) res6: java.lang.String = scala.collection.immutable.List[int] scala> def foo[A](a: A) = typeName(a) :5: error: could not find implicit value for parameter m:scala.reflect.Manifest[A]. def foo[A](a: A) = typeName(a) ^ scala> def foo[A](a: A)(implicit m: reflect.Manifest[A]) = typeName(a) foo: [A](a: A)(implicit m: scala.reflect.Manifest[A])java.lang.String scala> foo(Set("hello")) res8: java.lang.String = scala.collection.immutable.Set[java.lang.String] 

补充oxbow_lakes答案:这是不可能的,似乎永远不会发生(至少很快)。

JVM不支持具体化generics的(可反复的)原因似乎是:

  • 性能较低。
  • 它打破了向后兼容性。 它可以解决复制和修复很多库。
  • 它可以使用清单来实现:“解决方案”和最大的障碍。

参考文献:

  • Odersky在2010年发表评论 :“我更喜欢更简单的VM架构和类型擦除”

  • 在scala-internals名单中(2013年2月) Grzegorz Kossakowski说 :

您可以轻松地对其进行基准测试,并确保性能影响非常明显。 特别是内存消耗增加了很多。

我相信可行的方法是按照我们在Scala中使用Manifest / TypeTags开始的方式进行可选的设置。

如果可以将其与运行时专业化相结合,那么您可以瞄准高性能和通用代码。 但是,这可能是Scala 2.12或2.13的目标。

一旦scalac成为编译器,它就有可能使用实现具体化generics所需的任何数据结构来修饰生成的代码。

我的意思是scalac有能力看到……

 // definition class Klass[T] { value : T } //calls floats = Klass[float] doubles = Klass[double] 

……并“扩展”到这样的事情:

 // definition class Klass_float { value : float } class Klass_double { value : double } // calls floats = Klass_float doubles = Klass_double 

编辑

重点是:编译器能够创建所有必要的数据结构,certificate在运行时提供其他类型信息是必要的。 一旦此类型信息可用,Scala运行时将利用它并可以执行我们可以想象的所有类型感知操作。 JVM是否为已知的generics提供字节码并不重要。 这项工作不是由JVM完成的,而是由Scala库完成的。

如果您已经编写了一个符号调试器(我做过!),您就知道您可以将编译器在编译时具有的所有信息“转储”到生成的二进制文件中,采用任何数据组织演示,以便进一步处理。 这是完全相同的想法:’转储’Scala编译器具有的所有类型信息。

简而言之,我不明白为什么它不可能,无论JVM是否为具体化的generics提供本机操作都无关紧要。 JVM字节码与具体化的generics无关。 这类事情是语言规范,编译器function和运行时库支持的问题。

另一个编辑

IBM X10展示了我所说的能力:它将X10代码编译到Java代码上,将灵活的generics利用到Java平台上。 正如我之前提到的:它可以完成(和IBM X10一样!)但这种function涉及语言规范,编译器支持(或编译器插件)以及运行时库中的足够支持。 更多信息请访问: http : //x10.sourceforge.net/documentation/papers/X10Workshop2012/slides/Takeuchi.pdf