Overcoming type erasure in Scala
原文来自Overcoming type erasure in Scala。
本文旨在展示一些技术来解决由Scala泛型编程中的类型擦除引起的一些常见问题。
介绍
Scala有一个非常强大的类型系统,Scala是强类型语言。存在类型,结构类型,嵌套类型,路径依赖类型,抽象和具体类型成员,类型边界((upper, lower, view, context),使用站点和声明站点类型方差,支持类型多态(subtype, parametric, F-bounded, ad-hoc),更高级的类型,广义类型的约束……而且这个名单还在继续。
但是,即使Scala的类型系统在理论上非常强大,实际上一些与类型相关的特性由于其运行时环境的限制而受到了削弱 - 这就是类型擦除。
什么是类型擦除?简而言之,这是由Java和Scala编译器执行的一个过程,它在编译后删除所有的泛型类型信息。这意味着我们无法在运行时区分List [Int]
和List [String]
。为什么编译器会这样做?那么,因为Java虚拟机(运行Java和Scala的底层运行时环境)并不知道泛型。
类型擦除存在的历史原因。 Java从一开始就不支持泛型。所以当他们最终加入到Java 5中时,他们不得不保持向后兼容性。他们希望允许与旧的非通用遗留代码的无缝接口(这就是为什么我们有Java中的原始类型)。发生了什么是通用类中的类型参数被替换为Object或其上限。例如:
class Foo[T] {
val foo: T
}
class Bar[T <: Something] {
val bar: T
}
//-----type erasure
class Foo {
val foo: Object
}
class Bar {
val bar: Something
}
所以,运行时我们是不知道泛型类参数化的实际类。在我们的例子中,编译器只能看到原始的Foo和Bar。
不要认为类型擦除是某人无能或无知的产物。这不是坏的设计,而是一种权衡。
我想谈的是我们如何处理Scala中的类型擦除。不幸的是,没有办法防止类型擦除本身,但是我们会看到一些方法来解决它。
它是如何工作的(或不工作)
这里有一个简单的擦除类型的例子:
object Extractor {
def extract[T](list: List[Any]) = list.flatMap {
case element: T => Some(element)
case _ => None
}
}
val list = List(1, "string1", List(), "string2")
val result = Extractor.extract[String](list)
println(result)