Scala 謎題 - 有趣的類型轉換

在 Scala 中,List[String] 和 List[Int] 之間並無繼承關係,可是下面的代碼居然能夠經過編譯而且順利運行:html

object Test extends App {
  val strList: List[String] = List("a", "b", "c")
  val strToIntList: List[Int] = strList.asInstanceOf[List[Int]]
  println(strToIntList)
}

輸出:java

//輸出: List(a, b, c)

是的,你沒看錯!咱們把 List[String] 成功的轉換成了 List[Int] 類型。事實上真的是這樣嗎? 讓咱們來測試一下:express

object Test extends App {
  val strList: List[String] = List("a", "b", "c")
  val strToIntList: List[Int] = strList.asInstanceOf[List[Int]]
  val head = strToIntList(0)
  println(head)
}

輸出:api

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
	at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:101)
    ...
	at test.Test.main(Test.scala)

哈哈,拋出了類型轉換異常。編譯器推斷出 head 類型爲 Int 型,但在運行時卻被賦予了 String 型,因此致使了運行時錯誤。測試

翻看 Scala 文檔終於找到了緣由:scala

final def asInstanceOf[T0]: T0
Cast the receiver object to be of type T0.
Note that the success of a cast at runtime is modulo Scala's erasure semantics. Therefore the expression 1.asInstanceOf[String]will throw a ClassCastException at runtime, while the expression List(1).asInstanceOf[List[String]] will not. In the latter example, because the type argument is erased as part of compilation it is not possible to check whether the contents of the list are of the requested type.

在調用 asInstanceOf 方法時,編譯器給予開發者足夠的信任,認爲你有足夠的理由去這樣作。可是在運行時,因爲泛型類的類型參數被擦除了,因此 List[String] 和 List[Int] 在運行時都是 List 類型,可是在操做其元素時要格外當心,不然會拋出類型轉換異常。htm

利用這個特性咱們能夠寫出一些頗有意思的代碼,雖然 Class[T] 是 invariant 的,利用 asInstanceOf 方法可讓它變成 covariant,示例代碼以下:繼承

object Test extends App {
  val jsObjClass:   Class[JsObject] = classOf[JsObject]
  val jsValueClass: Class[JsValue]  = jsObjClass.asInstanceOf[Class[JsValue]]
}

因爲在運行時 JsObject 能夠被成功地轉換爲 JsValue,因此上述代碼能夠正常工做。開發

轉載請註明來自 PlayScala社區 。文檔

相關文章
相關標籤/搜索