为什么Scala的方法isInstanceOf[T]无法工作

3

为什么isInstanceOf[T]方法没有按预期工作?

以下是我定义的一个名为hello的类和伴生对象。在hello对象中,我在代码行"hel.typetest[Int]"中测试了this.isInstanceOf[T],当类型TInt时,为什么这个条件为true

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)
  }
}

输出:

main
true
class hello

1
通常使用isInstanceOf是不好的编程风格。它无法检查泛型类型(以及大多数集合)。文档:https://www.scala-lang.org/api/2.12.1/scala/Any.html#isInstanceOf[T0]:Boolean - Mikhail Ionkin
2个回答

8
由于类型擦除(连同装箱),T被擦除为Object,因此在字节码中this.isInstanceOf[T]变成了this.isInstanceOf[Object],始终为真。
恰好,ClassTag旨在避免这种情况,但您需要实际使用它而不是调用isInstanceOf
def typetest[T](implicit tag: ClassTag[T]): Unit = {
  Console.println(tag.runtimeClass.isInstance(this))
}

当存在ClassTag时,还有针对T的特殊情况support进行模式匹配:

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

有提议使 is/asInstanceOf[T] 在存在 ClassTag 时正常工作,但编译器中内置了假设,阻止了这一点,并且要更改这些假设太困难了(如果我记得原因正确的话)。


1
只是补充一下:ClassTag仅匹配更高的类型,而不匹配更高类型的参数类型。例如:valueToMatch match { case _: T => true case _ => false如果valueToMatch是一个List[Int],那么无论T是List[String]、List[Int]或任何其他类型的List,它都会始终打印true。 - asanand

0
在搜索了许多帖子并与类型擦除警告作斗争后,我找到了这个堆栈溢出答案模式匹配中的隐式ClassTag
基于那篇文章,我创建了以下隐式类型匹配器,以检查对象是否是泛型类型T的实例。希望这能帮助到某些人。
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
    }
  }
}

**编辑:经过更多的调查,您需要使用classTag[T].runtimeClass.isInstance(objectToCheck],而不是使用isInstanceOf[T],如Alexey Romanov的答案所指出的。

import scala.reflect.ClassTag

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接