返射:运行时的类信息

如果不知道某个对象的确切类型,RTTI可以告诉你。但是有一个限制:这个类型在编译时必须已知,这样才能使用RTTI识别它,并利用这些信息做一些有用的事。换句话说,在编译时,编译器必须知道所有要通过RTTI来处理的类。

初看起来这似乎不是限制,但是假设你获取了一个指向某个并不在你的程序空间中的对象引用;事实上,在编译时你的程序根本没法获知这个对象所属的类。例如,假设你从磁盘文件,或者网络连接中获取了一串字节,并且你被告知这些字节代表了一个类。即然这个类在编译器为你的程序生成代码之后很久才会出现,那么这样才能使用这样的类呢?

在传统的编程环境中不太可能出现这种情况。但当我们置身于更大规模的编程世界中,在许多重要情况下就会发生上面的情况。首先就是“基于构件的编程”,在此种编程方式中,将使用某种基于快速应用开发(RAD)的应用构建工具,即集成开发环境(IDE)来构建项目。这是一种可视化编程方法,可以通过将代表不同组件的图标拖曳到表单中来创建程序。然后在编程通过设置构件的属性值本配置它们。这种设计时的配置,要求构件都是可实例化的,并要暴露相关方法的信息,以便IDE能够帮助程序赠覆盖这些处理事件的方法。反射提供了一种机制--用来检查可用的方法,并返回方法名。Java通过JavaBeans提供了基于构件的编程架构。

人们想要在运行时获取类的信息的另一种动机,便是希望提供在跨网络的远程平台上创建和运行对象的能力。这被称为远程方法调用(RMI),它允许一个Java程序将对象分布到多台机器上。需要这种分布能力是有许多原因的,例如,你可能正在执行一项需进行大量计算的任务,为了提高运算速度,想将计算划分为许多小的计算单元,分布到空闲的机器上运行。又比如,你可能希望将待处理特定类型任务的代码(例如多层的C/S(客户端/服务器)架构中的“业务规则“),置于特定的机器上,于是这台机器就成为了这些动作的公共场所,可以很容易地通过改动它就达到影响系统中所有人的效果。(这是一种有趣的开发方式,因为机器的存在仅仅是为了方便软件的改动!)同时,分布式计算也支持适于执行特殊任务的专用硬件,例如矩阵转置,而这对于通用程序就显得不太合适或者太昂贵了。

Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含了FieldMethod以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。这样你就可以使用Constructor创建新的对象,用get()set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFieldsgetMethods()getConstructors()等很便利的方法,以返回表示字段,方法以及构造器的对象的数组(在JDK文档中,通过查找Class可了解更多相关资料)。这样,匿名对象的类信息就能在运行时被完全确定下来,而在统时不需要知道任何事情。

重要的是,要认识到反射机制并没有什么神奇之处。当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类(就像RTTI那样)。在用它做其他事情之前必须先加载那个类的Class对象。因此,那个类的.class文件对于JVM来说是必须是可获取的:要么在本地机器上,要么可以通过网络取得。所以RTTI和反射之间真正的区别只在于,对RTTI来说,编译器在编译时打开和检查.class文件。(换句话说,我们可以用“普通”方式调用对象的所有方法)而对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件。

results matching ""

    No results matching ""