Scala是否有可能在不更改JVM的情况下使用具体化的泛型?

时间:2009-08-31 15:04:33

标签: java generics scala jvm

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

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

5 个答案:

答案 0 :(得分:21)

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

当您询问“需要更改的内容是什么?”时,答案是:字节码规范。目前,字节码不允许定义变量的参数化类型。已经决定,作为字节码的修改,以支持具体化的泛型break backwards compatibility,即generics would have to be implemented via type erasure

为了解决这个问题,Scala利用其implicit机制的强大功能来定义 Manifest ,可以在任何范围内导入,以发现类型信息运行。清单是实验性的,很大程度上没有记录,但它们are coming as part of the library in 2.8。这是Scala reified generics / Manifests

上的另一个好资源

答案 1 :(得分:5)

为了补充oxbow_lakes,关于how to get around type erasure in Scala的Stack Overflow存在一个问题。

答案 2 :(得分:3)

“隐式Manifest”是一个Scala编译器技巧,它不会使Scala中的泛型具体化。 Scala编译器,当它看到一个带有“隐式m:Manifest [A]”参数的函数,并且它知道调用站点的A 的泛型类型时,它将包装A的类及其泛型将参数输入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)
<console>: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]

答案 3 :(得分:3)

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

JVM不支持具体化泛型的(可反驳)原因似乎是:

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

参考文献:

  

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

     

我相信要走的路是按照我们的方式进行可选的具体化   在Scala中用Manifest / TypeTags开始做。

     

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

答案 4 :(得分:1)

一旦 scalac 是一个编译器,它就有可能修饰生成的代码,无论使用什么数据结构来实现具体化的泛型。

我的意思是 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

修改

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

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

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

其他编辑

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