考虑以下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?
[剧透]这是一个
ClassCastException
;谁能解释为什么它没有被IllegalArgumentException
捕获并替换?PS:为了避免我为什么要这样做的任何疑问:这是某些代码的简化版本,该代码使用json4s将某些字符串解析为
Option[T]
;如果解析失败,则返回None
;如果T
是Unit
,则返回OK;如果T
是其他类型,则返回OK。 最佳答案
说明
这里没有抛出异常:
().asInstanceOf[T]
因为这是未经检查的强制转换-JVM无法验证是否可以将
()
强制转换为T
,因为由于类型擦除,它不具有关于T
的信息。而是在这里抛出异常
Data[Integer](None).get
因为
get
的结果被强制转换为Integer
,这是JVM可以验证的。因此,ClassCastException
实际上被抛出到get
之外。顺便说一句,
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(())
}
}
破解
对于此用例,可以直接检查
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