Search code examples
scalatype-parameter

Scala: Why asInstanceOf can accept a type parameter but isInstanceOf can not?


Here's the contrived experiments in REPL (scala 2.11):

scala> class Foo[T] {
     |   def as(x: Any) = x.asInstanceOf[T]
     | }

defined class Foo

scala> val foo = new Foo[String]
foo: Foo[String] = Foo@65ae6ba4

scala> val x: Any = 123
x: Any = 123

scala> foo.as(x)  // expected
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
  ... 33 elided

scala> val y: Any = "abc"
y: Any = abc

scala> foo.as(y)
res1: String = abc


scala> class Bar[T] {
     |   def is(x: Any) = x.isInstanceOf[T]
     | }

<console>:12: warning: abstract type T is unchecked since it is eliminated by erasure
         def is(x: Any) = x.isInstanceOf[T]
                                        ^
defined class Bar

scala> val bar = new Bar[String]
foo: Foo[String] = Foo@1753acfe

scala> val x: Any = 123
x: Any = 123

scala> bar.is(x)  // unexpected
res2: Boolean = true

scala> val y: Any = "abc"
y: Any = abc

scala> bar.is(y)
res3: Boolean = true

I know type parameter is quite limited due to type erasion, but still confused by the different behaviours between asInstanceOf and isInstanceOf here.

Wonder if someone has any insight about that? Thanks!


Solution

  • Well, you must know, that type parameters are not available at runtime, all information they carry can only be used by the compiler. Now, asInstanceOf is just a cast, it is only needed to the compiler to enforce the type compatibility, and at runtime it doesn't do anything at all: a reference is a reference, regarding of the type of underlying object.

    isInstanceOf on the other hand is the opposite: compiler does not know anything about it, it's just a function call. It is executed at runtime to check whether the given object is of the expected type. But the type parameter is not available at runtime, so how would it know which type to check against? For this reason, it needs a real Class argument.