类型转换前先做检查
迄今为止,我们已知的RTTI形式包括:
1.传统的类型转换,如“(Shape)”,由RTTI确保类型转换的正确性,如果你执行了一个错误的类型转换,就会抛出一个ClassCastException异常。
2.代表对象类型的Class对象。通过查询Class对象可以获取运行期所需的信息。
3. RTTI在Java中还有第三种形式,就是关键字instanceof。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。你可以用提问的方式使用它,就象这样:
if(x instanceof Dog)
((Dog)x).bark();
instanceod与Class的等价性
在查询类型信息时,以instanceof的形式(或者是以isInstance()的形式,它们产生相同的结果)与直接比较Class对象有一个很重要的差别。下面的例子向你展示了这种差别:
"Testing x of type class c10.Derived",
"x instanceof Base true",
"x instanceof Derived true",
"x.getClass() == Base.class false",
"x.getClass() == Derived.class true",
instanceof保持了类型的概念,它指的是“你是这个类吗,或者你是这个类的派生类吗?”而另一种情况是,如果你用==比较实际的Class对象,就不包含继承关系。
反射:运行时的类信息
Class类(本章前面已有论述)支持反射的概念,Java附带的库java.lang.reflect包含了Field,Method以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行期创建的,用以表示未知类里对应的成员。这样你就可以使用Constructor创建新的对象,用get()和set()方法读取和修改与Field对象关联的属性,用invoke()方法调用与Method对象关联的方法。另外,你还可以调用getFields(),getMethods(),getConstructors()等等很便利的方法,以返回表示属性、方法以及构造器的对象数组,这些对象(在JDK文档中,可找到与Class类相关的更多的资料)。这样,匿名对象的类信息就能在运行期被完全确定下来,而在编译期不需要知道任何事情。
重要的是,反射机制并没有什么魔法。当你通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类(就象RTTI那样)。但在这之后,在做其它事情之前,必须加载那个类的Class对象。因此,那个类的.class文件对于JVM来说必须是可获取的,要么在本地机器上,要么可以通过网络取得。
所以RTTI和反射之间真正的区别只在于,对RTTI来说,编译器在编译期打开和检查.class文件。(换句话说,我们可以用“普通”方式调用一个对象的所有方法。)而对于反射机制来说.class文件在编译期间是不可获取的,所以是在运行期打开和检查.class文件。