I was trying to answer this question, as I thought I knew the answer. Turns out, I did not know quite enough :/
Here is a test I have done:
class In
This is because the class-cast-exception is only thrown when you do something with the value, call a method on it after the cast. In the REPL for example, you would have a toString
call in the second case. Note:
new Inst[String].as(3); () // nothing happens
new Inst[String].as(3).toString; () // exception
The reason why this takes the extra step is that Inst[T]
is generic with type parameter T
which is erased at runtime; only when the call-site (that has a static knowledge of type T
) tries to call a method on the result, the actual type check occurs.
For your follow-up question, toString
is defined on any object and since T
is generic you have a boxed integer (<: AnyRef
) and toString
and println
succeed within the is
method. So another example where the Try
would fail is this:
class Inst[T] {
def foo(x: Any)(implicit ev: T <:< String) = scala.util.Try {
ev(as(x)).reverse
}.isSuccess
}
new Inst[String].foo(3) // false!
While @0__'s answer explains why it doesn't work, here is how to make it work:
class Inst[T](implicit tag: scala.reflect.ClassTag[T]) {
def is(x: Any) = tag.runtimeClass.isInstance(x)
// scala.util.Try { as(x) }.isSuccess will work as well
def as(x: Any): T = tag.runtimeClass.cast(x).asInstanceOf[T]
}
object Main extends App {
println(new Inst[String].is(3))
println(new Inst[String].as(3))
}
false
java.lang.ClassCastException: Cannot cast java.lang.Integer to java.lang.String
at java.lang.Class.cast(Class.java:3369)
...