Consider the following Scala code:
case class Data[T](value: Option[T]) {
def get: T = try {
doGet
} catch {
case e: Exception => throw new Illega
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.
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(())
}
}
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