有关instanceof及泛型类型信息的探究
在课上学习泛型时,老师提到不能使用instanceof这个运算符来检验泛型类型的信息,因为“运行时泛型消失了”:
一方面我不知道这个东西到底怎么使用,另一方面也不知道为什么要尽量避免使用。此外,在我的想象中泛型<E>中的E在使用时确定了类型以后会被替换掉,如果instanceof检测类型的话应该能检测出来这个确定的类型才对,然而现实中并不能。于是我决定对其进行一番探究,相信如果掌握了它的使用机制,既能在适当的时侯正确使用,又能帮助理解泛型的实现。
最开始我先尝试探究instanceof的作用域。显然,主数据类型是无法进行判断的(将Integer改成int也过不了编译):
但是Java为主数据类型提供了自动封装成对象的机制,所以直接定义成对象就没问题了:
那么如果换成父类呢?众所周知,Object是所有对象的父类。测试结果表明没问题:
为了保险起见以及后续的探究,我补充了两个简单的类以及一个接口,它们之间具有继承关系以及接口的使用:
首先搞了一个子类的对象,分别用本身这个类、父类和接口名进行测试:
结果是3个true,这说明无论是父类还是接口,只要继承或引用了,instanceof就都能体现出来。从逻辑上讲,这也是十分合理的:从中文翻译上来讲,instanceof的意思就是“本质上是”,那么继承父类的子类当然也是父类了,接口也是如此。接下来Animal的测试结果也没有什么可说的:
接下来一个很有价值的测试点就是多态。对于多态变量,重写和重载方法的选择机制已经比较复杂了,那instanceof会怎样呢?我声明了一个Animal类型的变量并将其实例化为Dog,再次进行了测试:
这回的结果仍然是3个true,说明“本质上是”的理解方式仍然行的通。虽然Mark被声明为了Animal,但是其实例化出来的是Dog,因为Dog继承了Animal又引用了Mammal接口,所以当然Mark在本质上也是Animal和Mammal。那么如果把Animal换成接口Mammal呢?
不出意外地,结果仍然是3个true,因为Mark本质上仍然是Dog,它被声明成啥并不重要。接下来测试测试泛型吧:
可以看到a被实例化为了只能装String的HashSet,无论是接口Set还是HashSet都能通过instanceof,因为a在本质上当然是HashSet也是Set。那么对于更为具体的Set<String>以及HashSet<String>呢?
结果是根本不能通过编译。在这里,IDE给出的错误提示告诉我们,一旦泛型被参数化了就不能再执行instanceof了,建议我们将尖括号里面的String改成问号,并解释说在运行时泛型类型信息会被抹掉。我先试了一下改成问号,果然没问题了:
所以看样子在运行时并不是将XX<E>中的E替换成某种具体的类型,而是把整个<E>抹掉了只剩下XX,这才能解释为什么我限定了括号里是String时不能通过编译而去掉或者换成通配符才行。这颠覆了我最初的想象!为了确保推理正确,我又去网上查找了资料,https://www.it1352.com/788122.html 这个网站验证了我的探究结果。
总结一下:instanceof的使用方法是看一个对象的“本质”,对主数据类型不适用;“本质”指的是实例化new的类,那个类的本身、其父类、引用的接口都能通过instanceof检测,这与这个对象被声明成什么类无关;泛型在运行时被抹掉的信息是括号中在使用时确定的类,因此当然不能用instanceof检测出具体的类型。
转载:https://wjrsbu.smartapps.cn/zhihu/article?id=122512889&isShared=1&_swebfr=1&_swebFromHost=baiduboxapp