2015-12-10 90 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) 
... 
相關問題