19

考虑以下 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,则不可以。T

4

1 回答 1

22

解释

这里没有抛出异常:

().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
于 2013-09-12T20:16:40.700 回答