透过Reflection,不需在程序中明定函数名称、自变量个数和类型
透过这个自我审视的过程,程序便能够了解它所要处理的对象(尤其是类型未知的对象),究竟具备了什么特质。对运用Reflection的程序而言,所了解到的这些特质,便会影响到该程序的运作行为。
取得了某类别的成员变量后(在Java中是以Field类别的对象表示),便可以取得该类别对象的成员变量值,也可以设定其值。同样的,取得了某类别的成员函数后(在Java中是以Method类别的对象表示),便可取得该成员函数的回传类型、传入的自变量列表类型,当然更重要的是,Method类别的对象,可被用以呼叫类别对象的相对应成员函数。
所以假想一个情境,你的程序面临了一个待处理的对象,但你完全不知道它是那个类型,有什么成员变量、有什么成员函数,但你还是可以察觉出这一切,你会知道每个成员变量的名称,每个成员函数的名称、甚至你还可以取得每个成员函数的值、设定它们的值、还可以呼叫每个成员函数,同时传入正确的自变量、正确地取得回传值。
除此之外,Java还允许程序人透过Class类别的newInstance()函数,产生该类别的对象,或许是透过Constructor类别对象取得建构式并呼叫、藉以执行不同建构式,以不同方式产生类别的对象。
从以上简短的描述中,你应当能够明白,Reflection让你得以在执行时期处理一些原先在编译时期才能够达成的动作。例如在Java中,你想要产生某个类别的对象,你得在程序中这么写:
Foo obj = new Foo();
编译时期就得将类别的名称明确写在程序中,也就是说,编译时期就必须让程序知道这件事。如果你想呼叫某个函数,你得这么写:
obj->bar(arg);
函数名称、自变量个数和类型,都必须在程序代码中明确指定。
但有了Reflection,便不再需要在程序代码中明确指定这些东西。例如,程序可以动态地决定究竟要产生那个类别的对象,你可以从设定档中读取类别的名称、根据使用者的输入值,经过一段逻辑运算之后,决定要产生的类别名称,接着再利用Reflection的机制,产生类别的对象。你也可以动态地得知产生出来的对象拥有那些成员函数,甚至是否具有特定名称的成员函数,接着呼叫这些函数。
有了Reflection,程序代码在撰写及编译的时间点,毋需明白实际在运行时,究竟会涉及那些类别以及它们各自的行为。你所写下的程序代码,可以完全是对要处理的类别一无所知,也可以是对他们有一点基本的假设(例如要处理的类别都具有相同名称的函数,却没有实作相同的接口,或是继承同样的类别),一切都可以等到执行时期,透过自我审视的能力,了解要面对的对象究竟具备什么特性,再依据相对应的逻辑,动态利用程序代码控制。 当程序毋需将行为写死,便消除了相依性
有了如此动态的能力,程序代码在撰写时毋需将行为写死,包括要处理的类别、要存取的成员变量、要呼叫的函数等。这大大增加了程序弹性,同时也增加了程序的扩充性。
举例来说,一个连接数据库的Java系统而言,在编译时期是不需要知道究竟运作时会使用那一个JDBC驱动程序,系统只需要透过某种方式,例如在设定档中指定类别名称,那么程序便可以依据这类别名称,加载相对应的JDBC驱动程序,程序代码中完全可以不涉及具体的JDBC驱动程序究竟为何。
这不仅消除了一定程度的相依性,相较于那些将数据库连接程序代码以静态的方式附属在程序代码中的做法,一旦遇上了必须变更的时候,上述的作法只需更动JDBC驱动程序在设定档中的名称,毋需改变任何已经编译出来的程序代码。
在不更动已编译好程序代码的情况下,大幅地影响程序的行为,便是Reflection的动态威力所在。
关于作者:
清华大学(台湾)资讯工程系的博士研究生,研究兴趣包括计算机网络、点对点网络、分布式网络管理、以及行动式代理人,专长则是Internet应用系统的开发。曾参与过的开发项目性质十分广泛而且不同,从ERP、PC Game到P2P网络电话都在他的涉猎范围之内。