Reputation: 93
Trying to cast a String
to a Double
obviously should fail:
scala> Try("abc".asInstanceOf[Double])
res11: scala.util.Try[Double] = Failure(java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Double)
However, if I define the above as a function:
scala> def convertAsTypeOf[T](anyValue: Any): Try[T] = Try(anyValue.asInstanceOf[T])
convertAsTypeOf: [T](anyValue: Any)scala.util.Try[T]
Strangely it returns Success when I try to cast a String
to a Double
:
scala> convertAsTypeOf[Double]("abc")
res10: scala.util.Try[Double] = Success(abc)
If I try to get the value out of Success, I get the following exception:
scala> convertAsTypeOf[Double]("abc").get
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Double
at scala.runtime.BoxesRunTime.unboxToDouble(BoxesRunTime.java:119)
Why does this happen? Is there any way to fix this and make asInstanceOf[T]
when it's in a generic function?
Upvotes: 2
Views: 1974
Reputation: 2155
The reason this happens is type erasure, which makes asInstanceOf[T] for generic T useless. I don't know why the compiler doesn't issue any warning.
You can force the check to happen by using a ClassTag. Something like this:
import reflect.ClassTag
def convertAsTypeOf[T : ClassTag](anyValue: Any): Try[T] = Try {
anyValue match { case t: T => t }
}
This works because the compiler will use the implicit ClassTag in the case match if it is available (and generate a warning if it is not).
Upvotes: 1
Reputation: 13667
Because of type erasure, the type T
is unknown at runtime, so asInstanceOf
can't actually check the cast that it does. In fact, it compiles as a no-op. However, when the .get
is finally done a cast is made to Double
, and because the type is known here we can get a ClassCastException
.
If you change convertAsTypeOf
to use ClassTag
:
def convertAsTypeOf[T](anyValue: Any)(implicit tag: ClassTag[T]): Try[T] =
Try(tag.runtimeClass.cast(anyValue).asInstanceOf[T])
You will then get errors when you expect:
scala> convertAsTypeOf[Double]("abc")
res1: scala.util.Try[Double] = Failure(java.lang.ClassCastException: Cannot cast java.lang.String to double)
The ClassTag
represents the type T
at runtime, allowing the value to be checked against it.
Upvotes: 4