为什么`.asInstanceOf`有时会抛出,有时不抛出?

时间:2015-12-10 14:31:46

标签: scala

我试图回答this问题,因为我认为我知道答案。 事实证明,我不太了解:/

这是我做过的测试:

class Inst[T] { 
  def is(x: Any) = scala.util.Try { as(x) }.isSuccess
  def as(x: Any): T = x.asInstanceOf[T]
}

scala> new Inst[String].is(3)
res17: Boolean = true

scala> new Inst[String].as(3)
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
 ... 33 elided

这里发生了什么?为什么只有第二次调用as抛出,而不是第一次调用?

2 个答案:

答案 0 :(得分:10)

这是因为只有在对值执行某些操作时才抛出class-cast-exception,在强制转换后调用方法。例如,在REPL中,您将在第二种情况下进行toString调用。注意:

new Inst[String].as(3); ()           // nothing happens
new Inst[String].as(3).toString; ()  // exception

这需要额外步骤的原因是Inst[T]是通用的,类型参数T在运行时被擦除;只有当调用站点(具有类型T的静态知识)尝试在结果上调用方法时,才会进行实际的类型检查。

对于您的后续问题,toString是在任何对象上定义的,因为T是通用的,所以您有一个盒装整数(<: AnyRef)和toString以及{{ 1}}在println方法中成功。因此is失败的另一个例子是:

Try

答案 1 :(得分:2)

虽然@ 0 __的答案解释了为什么它不起作用,但这是如何使它工作:

class Inst[T](implicit tag: scala.reflect.ClassTag[T]) {
  def is(x: Any) = tag.runtimeClass.isInstance(x) 
  // scala.util.Try { as(x) }.isSuccess will work as well
  def as(x: Any): T = tag.runtimeClass.cast(x).asInstanceOf[T]
}

object Main extends App {
  println(new Inst[String].is(3))
  println(new Inst[String].as(3))
}


false
java.lang.ClassCastException: Cannot cast java.lang.Integer to java.lang.String
    at java.lang.Class.cast(Class.java:3369)
...