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社区 。文档

相关文章
相关标签/搜索