CyberPlayerOne
CyberPlayerOne

Reputation: 3180

Why the Scala method isInstanceOf[T] is not working

Why the isInstanceOf[T] method is not working as intended?

In the following, I have defined a hello class and companion object. In the hello object, I test this.isInstanceOf[T] in the line of codes " hel.typetest[Int] ", how come this is true when the type T is Int?

object hello {
  def main(args: Array[String]): Unit = {
    Console.println("main")
    val hel = new hello
    hel.typetest[Int]
  }
}

class hello {
  def typetest[T: ClassTag]: Unit = {
    Console.println(this.isInstanceOf[T])
    Console.println(this.getClass)
  }
}

Output:

main
true
class hello

Upvotes: 3

Views: 938

Answers (2)

Jon Ediger
Jon Ediger

Reputation: 989

After searching lots of posts and fighting the type erasure warning, I came across this stack overflow answer Implicit ClassTag in pattern matching

Based on that post, I created the following implicit type matcher to check if an object is or is not an instance of the generic type T. Hope this helps someone.

import scala.reflect.ClassTag

object ObjectExtensions {
  implicit class IsType(obj: Object) {
    def isType[T](implicit tag: ClassTag[T]): Boolean = obj match {
      case tag(_: T) => true
      case _ => false
    }
  }
}

** edited: After more investigation, instead of using isInstanceOf[T] you need to use classTag[T].runtimeClass.isInstance(objectToCheck) as noted by Alexey Romanov's answer.

import scala.reflect.ClassTag

object ObjectExtensions {
  implicit class IsType(obj: Object) {
    def isType[T : ClassTag](): Boolean = {
      classTag[T].runtimeClass.isInstance(obj)
    }
  }
}

Upvotes: 0

Alexey Romanov
Alexey Romanov

Reputation: 170735

Because of type erasure (together with boxing). T erases to Object, so this.isInstanceOf[T] becomes this.isInstanceOf[Object] in bytecode which is always true.

As it happens, ClassTag is intended to avoid this, but you need to actually use it instead of calling isInstanceOf:

def typetest[T](implicit tag: ClassTag[T]): Unit = {
  Console.println(tag.runtimeClass.isInstance(this))
}

There's also special-case support for pattern-matching against T when a ClassTag is present:

def typetest[T: ClassTag]: Unit = {
  Console.println(this match {
    case _: T => true
    case _ => false
  })
}

There were proposals to make is/asInstanceOf[T] work correctly when a ClassTag is present too, but there are assumptions built into the compiler which prevent this and would be too hard to change (if I remember the reason correctly).

Upvotes: 8

Related Questions