2015-12-10 51 views
8

我试图回答this问题,因为我以为我知道答案。 事实证明,我不知道已经足够:/为什么`.asInstanceOf`有时会抛出,有时不会呢?

下面是测试我做过:

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,而不是第一个电话呢?

回答

10

这是因为类转换异常只在您对该值执行某些操作时引发,请在转换后调用该类上的方法。例如,在REPL中,您将在第二种情况下调用toString。注:

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

之所以这需要额外的步骤是Inst[T]是通用与在运行时删除类型参数T;只有当呼叫站点(具有T类型的静态知识)试图对结果调用方法时,才会发生实际的类型检查。


对于您的后续问题,toString定义任何对象,自T是通用的,你有一个盒装整数(<: AnyRef)和toStringprintlnis方法中取得成功。所以另一个例子,其中Try会失败是这样的:

class Inst[T] { 
    def foo(x: Any)(implicit ev: T <:< String) = scala.util.Try { 
    ev(as(x)).reverse 
    }.isSuccess 
} 

new Inst[String].foo(3) // false! 
+0

没有,这似乎并不以解释很多:我将'is'定义更改为:def is(x:Any)= scala.util.Try {as(x).toString} .isSuccess',并且它仍然返回'true'(即,投不投)。 即使这个'def是(x:Any)= scala.util.Try {println(as(x).toString)} .isSuccess;''愉快地打印出“3”并返回true: -/ – Dima

+0

请参阅我的编辑 –

+0

嗯,现在有道理,谢谢! 'is'不知道'T'是什么,所以它将这个参数视为'Any'。我试过这个:'特质Foo {def foo = ??? } class Inst [T <:Foo] {def is(x:Any)= scala.util.Try {as(x).foo} .isSuccess; def as(x:Any):T = x.asInstanceOf [T]; }'。现在'new Inst [Foo] .is(3)'按预期返回'false'。 – Dima

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) 
... 
相关问题