Scala 中的类型擦除
Type Erasure in Scala
我对这里发生的事情感到很困惑:
import scala.collection.immutable._
object Main extends App {
sealed trait Node
sealed trait Group
case class Sheet(
val splat: String,
val charname: String,
val children: ListMap[String, Node],
val params0: ListMap[String, Param], //params0 to separate sheet-general parameters
val note: Option[Note]
) extends Node with Group
case class Attributes(val name: String) extends Node with Group
case class Param(val name: String, val value: String) extends Node
case class Note(val note: String) extends Node
我有三个版本的替换函数 - 最后一个是我实际尝试编写的那个,其他的只是调试。
class SheetUpdater(s: Sheet) {
def replace1[T <: Group](g: T): Unit = {
s.children.head match {
case (_, _:Sheet) =>
case (_, _:Attributes) =>
}
}
}
这个版本没有发出警告,所以显然我可以在运行时访问 s.children
的类型。
class SheetUpdater(s: Sheet) {
def replace2[T <: Group](g: T): Unit = {
g match {
case _:Sheet =>
case _:Attributes =>
}
}
}
这个版本也没有,所以显然g
类型的详细信息也在运行时可用...
class SheetUpdater(s: Sheet) {
def replace3[T <: Group](g: T): Unit = {
s.children.head match {
case (_, _:T) => //!
case (_, _:Attributes) =>
}
}
}
...但即便如此,这最终还是给我带来了可怕的 Abstract type pattern T is unchecked since it is eliminated by erasure
警告。这是怎么回事?
在Scala中,泛型在运行时被擦除,这意味着List[Int]
和List[Boolean]
的运行时类型实际上是相同的。这是因为 JVM 作为一个整体擦除泛型类型。所有这一切都是因为 JVM 希望在首次引入泛型时保持向后兼容...
在 Scala 中有一种方法可以使用 ClassTag
来解决这个问题,它是一个隐式参数,然后可以与您正在使用的任何泛型一起使用。
您可以将 : ClassTag
视为将泛型类型作为参数传递。 (它是传递类型 ClassTag[T]
的隐式参数的语法糖。)
import scala.reflect.ClassTag
class SheetUpdater(s: Sheet) {
def replace3[T <: Group : ClassTag](g: T): Unit = {
s.children.head match {
case (_, _:T) => //!
case (_, _:Attributes) =>
}
}
}
Newer answers of this question have more details.
我对这里发生的事情感到很困惑:
import scala.collection.immutable._
object Main extends App {
sealed trait Node
sealed trait Group
case class Sheet(
val splat: String,
val charname: String,
val children: ListMap[String, Node],
val params0: ListMap[String, Param], //params0 to separate sheet-general parameters
val note: Option[Note]
) extends Node with Group
case class Attributes(val name: String) extends Node with Group
case class Param(val name: String, val value: String) extends Node
case class Note(val note: String) extends Node
我有三个版本的替换函数 - 最后一个是我实际尝试编写的那个,其他的只是调试。
class SheetUpdater(s: Sheet) {
def replace1[T <: Group](g: T): Unit = {
s.children.head match {
case (_, _:Sheet) =>
case (_, _:Attributes) =>
}
}
}
这个版本没有发出警告,所以显然我可以在运行时访问 s.children
的类型。
class SheetUpdater(s: Sheet) {
def replace2[T <: Group](g: T): Unit = {
g match {
case _:Sheet =>
case _:Attributes =>
}
}
}
这个版本也没有,所以显然g
类型的详细信息也在运行时可用...
class SheetUpdater(s: Sheet) {
def replace3[T <: Group](g: T): Unit = {
s.children.head match {
case (_, _:T) => //!
case (_, _:Attributes) =>
}
}
}
...但即便如此,这最终还是给我带来了可怕的 Abstract type pattern T is unchecked since it is eliminated by erasure
警告。这是怎么回事?
在Scala中,泛型在运行时被擦除,这意味着List[Int]
和List[Boolean]
的运行时类型实际上是相同的。这是因为 JVM 作为一个整体擦除泛型类型。所有这一切都是因为 JVM 希望在首次引入泛型时保持向后兼容...
在 Scala 中有一种方法可以使用 ClassTag
来解决这个问题,它是一个隐式参数,然后可以与您正在使用的任何泛型一起使用。
您可以将 : ClassTag
视为将泛型类型作为参数传递。 (它是传递类型 ClassTag[T]
的隐式参数的语法糖。)
import scala.reflect.ClassTag
class SheetUpdater(s: Sheet) {
def replace3[T <: Group : ClassTag](g: T): Unit = {
s.children.head match {
case (_, _:T) => //!
case (_, _:Attributes) =>
}
}
}
Newer answers of this question have more details.