【问题标题】:Checking scala types at runtime and type erasure在运行时检查 scala 类型和类型擦除
【发布时间】:2017-05-17 11:03:00
【问题描述】:

假设我们有几个这样的类:

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 突然总是返回 true

scala>new Rock winsOver new Rock
res0: Boolean = true

我一直试图弄清楚这一点,并且根据我的发现,这是因为 JVM 没有携带尽可能多的类型信息。这会导致一些信息丢失(“擦除”),并且有一些方法可以在 scala 中解决这个问题,以前使用清单,现在使用类标签和类型标签。

我真的无法更具体地弄清楚它是如何工作的,虽然我有时能够从互联网上复制代码 sn-ps 来做类似的事情,但我并不真正理解该代码是如何工作的我无法适应这个例子。我还注意到有一个无形库对这种东西有很多支持,但我也想了解它是如何工作的。

【问题讨论】:

    标签: scala types erasure


    【解决方案1】:

    您不应该在运行时检查类型信息。 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
    

    【讨论】:

      【解决方案2】:

      简单的解决方案是

      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 模式对这种情况有特殊支持。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-30
        • 2012-05-28
        • 1970-01-01
        • 2017-07-20
        • 1970-01-01
        • 2019-04-10
        相关资源
        最近更新 更多