在运行时检查scala类型并键入擦除

时间:2017-05-17 11:03:01

标签: scala types erasure

所以说我们有几个这样的课程:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td> </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td></td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td></td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
  <tr><td>one</td><td>&nbsp; &nbsp; &nbsp; </td><tr>
</table>

这有效

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

我一直试图解决这个问题,而且从我发现的情况来看,这是因为JVM不会携带尽可能多的类型信息。这导致一些信息丢失(“擦除”),并且有一些方法可以解决这个问题,先前有清单,现在有classtags和typetags。

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

2 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:0)

简单的解决方案是

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模式对此案例有特殊支持。