考虑以下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;如果TUnit,则返回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

08-25 10:57