在运行时检查 Scala 类型和类型擦除
Checking scala types at runtime and type erasure
假设我们有几个 类 像这样:
abstract class Throw {
def winsOver(t2: Throw): Boolean
}
class Rock extends Throw {
override def winsOver(t2: Throw): Boolean = t2 match {
case _: Scissors => true
case _ => false
}
}
class Scissors extends Throw {
override def winsOver(t2: Throw): Boolean = t2 match {
case _: Paper => true
case _ => false
}
}
class Paper extends Throw {
override def winsOver(t2: Throw): Boolean = t2 match {
case _: Rock => true
case _ => false
}
}
这个有效
scala>new Paper winsOver new Rock
res0: Boolean = true
scala>new Rock winsOver new Rock
res1: Boolean = false
但是,代码有很多重复。由于唯一不同的是他们击败的类型,我们可以尝试将其排除在外
abstract class Throw {
type Beats <: Throw
def winsOver(t2: Throw): Boolean = t2 match {
case _: Beats => true
case _ => false
}
}
class Rock {
type Beats = Scissors
}
class Scissors {
type Beats = Paper
}
class Paper {
type Beats = Rock
}
但是编译器开始报错
warning: abstract type pattern Throw.this.Beats is unchecked since it is eliminated by erasure
case _: Beats => true
果然,它不起作用。 winsOver
突然总是returns真
scala>new Rock winsOver new Rock
res0: Boolean = true
我一直在努力解决这个问题,根据我的发现,这是因为 JVM 没有尽可能多地携带类型信息。这会导致一些信息丢失 ("erasure"),并且在 Scala 中有一些方法可以解决这个问题,以前使用清单,现在使用类标签和类型标签。
我真的无法更具体地弄清楚这是如何工作的,虽然有时我能够从 Internet 上复制代码片段来做类似的事情,但我真的不明白该代码是如何工作的我不能让它适应这个例子。我还注意到有一个 shapeless 库对这种东西有很多支持,但我也想了解它是如何工作的。
简单的解决方案是
import scala.reflect.ClassTag
abstract class Throw[Beats <: Throw : ClassTag] {
def winsOver(t2: Throw): Boolean = t2 match {
case _: Beats => true
case _ => false
}
}
class Rock extends Throw[Scissors]
...
: ClassTag
是上下文绑定,这意味着隐式 ClassTag[Beats]
可用,并且 : Beats
模式对这种情况有特殊支持。
您不应该在运行时检查类型信息。 Type erasure is a good thing 并且 Scala 应该擦除比它更多的类型。
改为使用代数数据类型和模式匹配:
sealed abstract class Throw {
def winsOver(t2: Throw): Boolean
}
case object Rock extends Throw {
def winsOver(t2: Throw): Boolean = t2 match {
case Scissors => true
case _ => false
}
}
case object Scissors extends Throw {
def winsOver(t2: Throw): Boolean = t2 match {
case Paper => true
case _ => false
}
}
case object Paper extends Throw {
def winsOver(t2: Throw): Boolean = t2 match {
case Rock => true
case _ => false
}
}
这有一些重复,所以我们可以把它分解出来:
sealed abstract class Throw {
def winsOver(t2: Throw): Boolean = (this,t2) match {
case (Paper, Rock) | (Rock, Scissors) | (Scissors,Paper) => true
case _ => false
}
}
case object Rock extends Throw
case object Scissors extends Throw
case object Paper extends Throw
这按预期工作:
scala> Rock winsOver Scissors
res0: Boolean = true
scala> Paper winsOver Scissors
res1: Boolean = false
假设我们有几个 类 像这样:
abstract class Throw {
def winsOver(t2: Throw): Boolean
}
class Rock extends Throw {
override def winsOver(t2: Throw): Boolean = t2 match {
case _: Scissors => true
case _ => false
}
}
class Scissors extends Throw {
override def winsOver(t2: Throw): Boolean = t2 match {
case _: Paper => true
case _ => false
}
}
class Paper extends Throw {
override def winsOver(t2: Throw): Boolean = t2 match {
case _: Rock => true
case _ => false
}
}
这个有效
scala>new Paper winsOver new Rock
res0: Boolean = true
scala>new Rock winsOver new Rock
res1: Boolean = false
但是,代码有很多重复。由于唯一不同的是他们击败的类型,我们可以尝试将其排除在外
abstract class Throw {
type Beats <: Throw
def winsOver(t2: Throw): Boolean = t2 match {
case _: Beats => true
case _ => false
}
}
class Rock {
type Beats = Scissors
}
class Scissors {
type Beats = Paper
}
class Paper {
type Beats = Rock
}
但是编译器开始报错
warning: abstract type pattern Throw.this.Beats is unchecked since it is eliminated by erasure
case _: Beats => true
果然,它不起作用。 winsOver
突然总是returns真
scala>new Rock winsOver new Rock
res0: Boolean = true
我一直在努力解决这个问题,根据我的发现,这是因为 JVM 没有尽可能多地携带类型信息。这会导致一些信息丢失 ("erasure"),并且在 Scala 中有一些方法可以解决这个问题,以前使用清单,现在使用类标签和类型标签。
我真的无法更具体地弄清楚这是如何工作的,虽然有时我能够从 Internet 上复制代码片段来做类似的事情,但我真的不明白该代码是如何工作的我不能让它适应这个例子。我还注意到有一个 shapeless 库对这种东西有很多支持,但我也想了解它是如何工作的。
简单的解决方案是
import scala.reflect.ClassTag
abstract class Throw[Beats <: Throw : ClassTag] {
def winsOver(t2: Throw): Boolean = t2 match {
case _: Beats => true
case _ => false
}
}
class Rock extends Throw[Scissors]
...
: ClassTag
是上下文绑定,这意味着隐式 ClassTag[Beats]
可用,并且 : Beats
模式对这种情况有特殊支持。
您不应该在运行时检查类型信息。 Type erasure is a good thing 并且 Scala 应该擦除比它更多的类型。
改为使用代数数据类型和模式匹配:
sealed abstract class Throw {
def winsOver(t2: Throw): Boolean
}
case object Rock extends Throw {
def winsOver(t2: Throw): Boolean = t2 match {
case Scissors => true
case _ => false
}
}
case object Scissors extends Throw {
def winsOver(t2: Throw): Boolean = t2 match {
case Paper => true
case _ => false
}
}
case object Paper extends Throw {
def winsOver(t2: Throw): Boolean = t2 match {
case Rock => true
case _ => false
}
}
这有一些重复,所以我们可以把它分解出来:
sealed abstract class Throw {
def winsOver(t2: Throw): Boolean = (this,t2) match {
case (Paper, Rock) | (Rock, Scissors) | (Scissors,Paper) => true
case _ => false
}
}
case object Rock extends Throw
case object Scissors extends Throw
case object Paper extends Throw
这按预期工作:
scala> Rock winsOver Scissors
res0: Boolean = true
scala> Paper winsOver Scissors
res1: Boolean = false