Consider the following Scala code:
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?
[spoiler] It is a ClassCastException
; who can explain why it is not caught and replaced by an IllegalArgumentException
?
PS: To preempt any questions on why I would want to do this: this is a simplified version of some code that uses json4s to parse some string into an Option[T]
; if the parsing fails None
is returned, which is OK if T
was Unit
and not OK if T
is some other type.
Explanation
Exception isn't thrown here:
().asInstanceOf[T]
because this is an unchecked cast - JVM cannot verify if it is possible to cast ()
into T
, because it has no information about T
due to type erasure.
Instead, exception is thrown here
Data[Integer](None).get
because the result of get
is cast into an Integer
and that is something that JVM can verify. So, ClassCastException
is actually thrown outside of get
.
BTW, javac
always warns about unchecked casts, I don't know why scalac
doesn't.
Workaround
To some extent, it is possible to work around type erasure here using ClassTag
and reflection-based casting:
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(())
}
}
Hackaround
For this use case, you can inspect the ClassTag
directly:
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