我有以下声明。
val a: Any = Array("1", "2", "3")
a match {
case p: Array[Int] => println("int")
case l: Array[String] => println("string")
}
val b: Any = List(1, 2, 3)
b match {
case l: List[String] => println("string")
case p: List[Int] => println("int")
}第一个关于Array的代码块编译时没有警告,并输出"string“;第二个关于List的代码块编译时出现了与类型擦除相关的警告,也输出了"string”。
我对JVM中的类型擦除有所了解。在运行时,JVM不能真正知道容器的泛型类型(比如List)。但是为什么Array可以避免运行时的类型擦除,并获得正确的类型匹配?
我试图从scala源代码中找到答案。我发现的唯一一件事是数组使用ClassTag,而List不使用。
我想知道ClassTag是如何工作的。ClassTag是擦除类型的变通方法吗?为什么像List这样的容器还没有用ClassTag实现,以避免类型擦除。
发布于 2015-09-23 06:21:18
Scala在JVM上运行并继承其约束。Java使用类型擦除,因此所有参数化的类型在运行时都是相同的。类型信息将从它们中删除。这样做是为了保持与根本不能使用类型参数的旧Java版本的兼容性。
但是数组是Java中的特例,它们保存类型信息。scala数组就是这样做的。这对于在数组中保持内存高效的未装箱的值是必要的。
你应该假设所有的类型信息在运行时都丢失了。所以使用一些标签来匹配它们。
ClassTag与数组换行无关。所有类型信息都是由JVM本身提供的。
每次表达类型关系遇到困难时,Java语言中都有使用AnyRef和动态转换的习惯。Scala为静态描述类型提供了更具表现力的能力,而无需运行时转换。Scala编码风格鼓励使用重类型结构来保证代码的类型安全。
ClassTag和TypeTag是只能与静态类型代码一起使用的工具。它们包含编译器在编译时派生的类和类型信息。如果它可以静态地派生类型,那么它就可以为您提供类型标签来访问这些类型。
当你写了一些库,却不知道如何使用它时,这是很有用的。因此,您需要ClassTag作为隐式参数,它将由编译器根据提供给函数调用的其他参数使用适当的类型进行填充。隐式参数由库代码根据需要放置,并由调用库的外部代码自动填充。
发布于 2015-09-23 16:10:58
在这些情况下,您可能需要考虑使用通过shapeless获得的Typeable类型类进行类型安全转换。例如:
scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._
scala> val b: Any = List(1, 2, 3)
b: Any = List(1, 2, 3)
scala> b.cast[List[String]]
res1: Option[List[String]] = None
scala> b.cast[List[Int]]
res2: Option[List[Int]] = Some(List(1, 2, 3))正如您可以通过shapeless通过implicits添加到每个类型中的cast[T]方法一样,如果转换失败,则返回一个值为None的Option[T],如果转换成功,则返回Some。
如果你喜欢,你可以看看Typeable的源代码。然而,我建议你在做之前先喝一杯好咖啡。:)
https://stackoverflow.com/questions/32727779
复制相似问题