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