Lets say I have an array like this*:
val foo: Any = 1 : Int
Option(foo.asInstanceOf[String])
which fails for obvious reason:
// java.lang.ClassCastException: java.lang.Integer cannot be cast to
// java.lang.String
// ... 48 elided
Next lets consider a following class:
case class DummyRow() {
val foo: Any = 1 : Int
def getAs[T] = foo.asInstanceOf[T]
def getAsOption[T] = Option(foo.asInstanceOf[T])
}
As far as I can tell getAs
should behave the same way as the previous apply
followed by asInstanceOf
.
Surprisingly it is not the case. When called alone it throws an exception:
DummyRow().getAs[String]
// java.lang.ClassCastException: java.lang.Integer cannot be cast to
// java.lang.String
// ... 48 elided
but when wrapped with Option
succeeds:
val stringOption = Option(DummyRow().getAs[String])
// Option[String] = Some(1)
DummyRow().getAsOption[String]
// Option[String] = Some(1)
and fails only when I try to access wrapped value:
stringOption.get
// java.lang.ClassCastException: java.lang.Integer cannot be cast to
// java.lang.String
// ... 48 elided
So what happens here? It seems to be limited ClassCastException
so I guess it is related to some ugly thing like type erasure.
* Any
and asInstanceOf
are there to mimic a behavior of the 3rd party code so please lets not dwell on that.
** Tested in Scala 2.10.5, 2.11.7
*** If you're interested in the context you can take a look at Using contains in scala - exception
**** Other relevant questions linked in the comments:
Below is a simplified version of your problem with an additional case for Any
def getAs[T] = (1:Int).asInstanceOf[T]
//blows up
getAs[String]
//blows up
def p(s:String): Unit = {}
p(getAs[String])
//works
def p[T](s:T): Unit = {}
p(getAs[String])
//works
def p(s:Any): Unit = {}
p(getAs[String])
Because you create a method with a generic parameter, the runtime doesn't need to "touch" the value because it does not care. Generic will be treated as Any
/Object
at runtime.