请考虑以下Scala代码:
case class Data[T](value: Option[T]) {
def get: T = try {
doGet
} catch {
case e: Exception => throw new IllegalArgumentException
}
def doGet: T = value match {
case Some(v) => v
case None => ().asInstanceOf[T]
}
}
Data[Unit](None).get
Data[Integer](None).get // which exception is thrown here?
Run Code Online (Sandbox Code Playgroud)
[剧透]这是一个ClassCastException; 谁能解释为什么它没被捕获并被一个IllegalArgumentException?
PS:要抢占任何关于我为什么要这样做的问题:这是一些代码的简化版本,它使用json4s将一些字符串解析成一个Option[T]; 如果解析失败None则返回,如果T是Unit,那就没关系,如果T是其他类型则不行.
ghi*_*hik 22
不抛出异常:
().asInstanceOf[T]
Run Code Online (Sandbox Code Playgroud)
因为这是一个未经检查的强制转换 - JVM无法验证是否可以强制()转换T,因为它没有关于T类型擦除的信息.
相反,抛出异常
Data[Integer](None).get
Run Code Online (Sandbox Code Playgroud)
因为结果get被强制转换成了一个IntegerJVM可以验证的东西.所以,ClassCastException实际上是扔在外面的get.
BTW,javac总是警告未经检查的演员,我不知道为什么scalac不.
在某种程度上,可以使用ClassTag基于反射的转换来解决类型擦除问题:
import scala.reflect.{ClassTag, classTag}
case class Data[T: ClassTag](value: Option[T]) {
def get: T = try {
doGet
} catch {
case e: Exception => throw new IllegalArgumentException
}
def doGet: T = value match {
case Some(v) => v
case None => classTag[T].runtimeClass.asInstanceOf[Class[T]].cast(())
}
}
Run Code Online (Sandbox Code Playgroud)
对于此用例,您可以ClassTag直接检查:
scala> case class Data[T](value: Option[T])(implicit t: ClassTag[T]) {
| def get: T = value getOrElse (t match {
| case ClassTag.Unit => ().asInstanceOf[T]
| case _ => throw new IllegalArgumentException
| })
| }
defined class Data
scala> Data[Unit](None)
res6: Data[Unit] = Data(None)
scala> .get
scala> Data[Int](None).get
java.lang.IllegalArgumentException
Run Code Online (Sandbox Code Playgroud)