作者:仙人掌工作室
Java映像API(Reflection API)和Java 接口为编写可重用的代码提供了优秀的工具。以一个通用的命令启动器为例:假设你有一组执行各种任务的类,比如关闭或打开电灯,打开、关闭或锁上门,等等。这些类的名字分别是LightOn、LightOff、DoorOpen、DoorClose和DoorLock,所有这些类都实现了Command接口。
Command接口的定义如下:
public interface Command {
public void process();
}
你可以编写一个简单的通用启动器,如下所示:
public class Launcher{
public static void main(String[] args){
if (args.length>0) {
try {
Command command =
(Command)Class.forName(args[0]).newInstance();
command.process();
} catch (Exception ex) {
System.out.println("Invalid command");
}
} else {
System.out.println("Usage: Launcher ");
}
}
// Launcher
这个程序用Class.forName方法获得参数中指定类的Class对象,然后用newInstance()方法创建该类的一个实例。根据要求,该类实现了Command接口,所以程序把对象定型(cast)成为Command,然后调用process()方法,由process方法执行实际任务。假如出现了异常,比如由于类的名字拼写错误或安全方面的问题,程序将显示一个“Invalid command”信息。
这个命令启动器可以按照如下方式使用:
%java Launcher LightOn
以后假如实现了一些新的任务,命令启动器也不需要修改。从程序员的角度来看,这确实很不错。但是,它对于用户来说又如何呢?假设一个用户输入了以下命令:
%java Launcher OpenDoor
Invalid command
“Invalid command”的意思是用户不能打开门吗?不是,它只表示类命名错误(DoorOpen变成了OpenDoor)。所以,程序应该答应用户查看可用命令的清单。要保证命令启动器的通用性,用户应该能够在运行时查找这些命令。
Java映像API能够在运行时提供大量有关指定类的信息:我们可以方便地获知指定类的所有超类、它所实现的接口、方法、构造函数、域,等等。但在这里,我们感爱好的是所有实现特定接口的类,这种信息无法从Java映像API直接获得。本文余下的部分就为你介绍如何获取实现了特定接口的类的信息。
改进通用命令启动器的用户界面 在Java中,包对应着目录,通过File对象的list()方法获取包含在包中的所有类是很轻易的。我们的做法是利用instanceof语句进行检查:对于包里面的每一个类文件,相应的类是否实现了Command接口。这意味着只检查每一个类文件的公用类,而且接口和它的实现必须在一个包里面。下面是代码: public static void find(String pckgname) {
// 把包名字转换成绝对路径
String name = new String(pckgname);
if (!name.startsWith("/")) {
name = "/" + name;
}
name = name.replace(′.′,′/′);
// 获得一个File对象
URL url = Launcher.class.getResource(name);
File Directory = new File(url.getFile());
if (directory.exists()) {
// 获得包里面的文件清单
String [] files = directory.list();
for (int i=0;I<files.length;i++) {
// 我们只对.class文件感爱好
if (files[i].endsWith(".class")) {
// 删除.class文件扩展名
String classname = files[i].substring(0,files[i].length()-6);
try {
// 尝试创建该对象的一个实例
Object o = Class.forName(pckgname+"."+classname).newInstance();
if (o instanceof Command) {
System.out.println(classname);
}
} catch (ClassNotFoundException cnfex) {
System.err.println(cnfex);
} catch (InstantiationException iex) {
// 我们试图实例化一个接口或者
// 一个没有默认构造函数的对象
} catch (IllegalAccessException iaex) {
// 该类不是公用类
}
}
}
}
}
要执行手头的任务,我们只需稍微修改一下原来的启动器。