相 信 有 不 少 人 使 用c 语 言 的 函 数 指 针 实 现 过 函 数 的 动 态 调 用。 适 当 地 运 用 函 数 动 态 调 用 功 能 不 仅 能 减 少 代 码 数 量, 而 且 对 于 增 加 程 序 的 可 读 性 和 易 维 护 性 也 带 来 极 大 帮 助。 由 于java 不 支 持 指 针 功 能, 所 以 运 用 指 针 实 现 动 态 调 用 不 适 用 于java。 本 文 提 出 了 一 种 运 用java reflection api 实 现java 函 数 动 态 调 用 的 方 法, 并 给 出 一 个 基 本 实 现 和 测 试 实 例。
---- 函 数 动 态 调 用 的 一 个 最 为 常 见 的 应 用 场 合 在 于 取 消 对 于switch 关 键 词 的 依 赖。switch 关 键 词 用 来 判 断 条 件, 并 分 别 给 出 处 理 过 程 即 处 理 函 数。 一 个 典 型 的 函 数 具 有 函 数 名、 输 入 值、 返 回 值。 而 实 现 动 态 调 用 的 关 键 之 处 在 于 调 用 时 得 到 正 确 的 类 名、 函 数( 方 法) 名、 以 统 一 的 格 式 实 现 参 数 的 传 递。 下 面 是 实 现 该 功 能 的 类invoker,argumentholder 的 清 单:
//--------------------------------------
//类invoker
//实现函数的动态调用
//方法:
// dynacall
// 参数 object c希望调用函数(方法)所在对象
// string m希望调用的方法名称
// argumentholder a传递给被调用方法的参数
//-----------------------------------------
import java.lang.reflect.invocationtargetexception;
import java.lang.reflect.method;
public class invoker {
static object dynacall(object c,
string m, argumentholder a)
throws nosuchmethodexception,
invocationtargetexception,
illegalaccessexception {
//由于java支持方法名称的重载
(同一类中出现多于一个的同名函数),
//所以getmethod方法使用两个参数:
字符串形式的方法名称和数组形式
//的调用参数类列表。返回值为类method的一个对象,
该对象和将要被
//调用的方法相关联
method meth = c.getclass().getmethod
(m, a.getargumentclasses());
//通过类method对象的invoke方法动态调用希望调用的方法
return (meth.invoke(c, a.getarguments()));
}
}
//-------------------------------------
//类argumentholder
//用于调用参数的封装,实现变长参数及
不同类型参数的统一形式地传递
//成员变量:
// class[] cl 参数类型数组
// object[] args 参数对象数组
//方法:
// getargumentclasses()返回参数类型数组
// getarguments() 返回参数对象数组
// setargument() 在参数列表中增加项目
//
//---------------------------------------
public class argumentholder {
protected class[] cl;
protected object[] args;
protected int argc;
argumentholder() {
argc = 0;
cl = new class[0];
args = new object[0];
}
argumentholder(int argc) {
this.argc = argc;
cl = new class[argc];
args = new object[argc];
}
public class[] getargumentclasses() {
return cl;
}
public object[] getarguments() {
return args;
}
//以下16个setargument
函数实现简单数据类型boolean,byte,
// char,int,short,long,float,double的封装。
为支持invoker
//类dynacall方法中getclass的调用,
此处将简单数据类型
//转换为对应的对象,如boolean转换为boolean
public int setargument(boolean b) {
return this.setargument(argc, new boolean(b),
boolean.type);
}
public int setargument(int argnum, boolean b) {
return this.setargument(argnum, new boolean(b),
boolean.type);
}
public int setargument(byte b) {
return this.setargument(argc, new byte(b),
byte.type);
}
public int setargument(int argnum, byte b) {
return this.setargument(argnum, new byte(b),
byte.type);
}
public int setargument(char c) {
return this.setargument(argc, new character(c),
character.type);
}
public int setargument(int argnum, char c) {
return this.setargument(argnum, new character(c),
character.type);
}
public int setargument(int i) {
return this.setargument(argc, new integer(i),
integer.type);
}
public int setargument(int argnum, int i) {
return this.setargument(argnum, new integer(i),
integer.type);
}
public int setargument(short s) {
return this.setargument(argc, new short(s),
short.type);
}
public int setargument(int argnum, short s) {
return this.setargument(argnum, new short(s),
short.type);
}
public int setargument(long l) {
return this.setargument(argc, new long(l),
long.type);
}
public int setargument(int argnum, long l) {
return this.setargument(argnum, new long(l),
long.type);
}
public int setargument(float f) {
return this.setargument(argc, new float(f),
float.type);
}
public int setargument(int argnum, float f) {
return this.setargument(argnum, new float(f),
float.type);
}
public int setargument(double d) {
return this.setargument(argc, new double(d),
double.type);
}
public int setargument(int argnum, double d) {
return this.setargument(argnum, new double(d),
double.type);
}
//以下2个setargument函数实现对象的封装,
public int setargument(object obj) {
return this.setargument(argc, obj,
obj.getclass());
}
public int setargument(int argnum, object obj) {
return this.setargument(argnum, obj,
obj.getclass());
}
//以下setargument函数具体实现以对象形式
提供的参数封装。
//具体操作为:增加计数器argc的值、
在cl和args中增加相应内容
public int setargument(int argnum,
object obj, class c) {
if (argnum >= args.length) {
argc = argnum + 1;
class[] clexpanded = new class[argc];
object[] argsexpanded = new object[argc];
system.arraycopy(cl, 0, clexpanded,
0, cl.length);
system.arraycopy(args, 0, argsexpanded,
0, args.length);
cl = clexpanded;
args = argsexpanded;
}
args[argnum] = obj;
cl[argnum] = c;
return argnum;
}
}
---- 以 下 给 出 一 个 类invoker 和argumentholder 的 应 用 实 例。 类dynacalltest 应 用 类invoker 和argumentholder 实 现 以 下 功 能: 根 据 用 户 在 命 令 行 中 输 入 的 内 容, 动 态 地 确 定 所 调 用 方 法。 程 序 可 以 接 受 的 合 法 输 入 为:
add 参数为两个整数,显示两个整数之和
concat 参数为两个字符串,显示两个字符串连接之后的值
help 无参数,显示可以接受的命令列表
minmax 参数为三个整数,显示三个参数的最大之和最小值
quit 无参数,结束运行
rand 无参数,显示一个随机值
程序清单如下:
import java.io.bufferedreader;
import java.io.inputstreamreader;
import java.io.ioexception;
import java.lang.reflect.invocationtargetexception;
import java.lang.reflect.method;
import java.lang.math;
import java.util.random;
import java.util.stringtokenizer;
public class dynacalltest {
random rand;
bufferedreader consolein;
//建构函数,初始化
dynacalltest() {
consolein = new bufferedreader
(new inputstreamreader(system.in));
rand = new random();
}
//读取用户输入行
string getcommandline() throws ioexception {
system.out.print(">");
system.out.flush();
string commandline = consolein.readline();
if (commandline == null)
return "quit";
if (commandline.length() == 0)
return "quit";
else
return commandline;
}
//从用户输入行中提取命令,
格式化为:首字符大写,其余字符小写
//返回格式化后的命令
string extractcommand(string commandline) {
stringtokenizer t = new stringtokenizer(commandline);
string cmd = t.nexttoken().tolowercase();
return cmd.substring(0,1).touppercase()
+ cmd.substring(1);
}
//从用户输入行中提取参数,
创建argumentholder类型的对象a以封装所有参数
//错误格式的整数被视为字符串
//返回argumentholder类型的对象,即封装后的参数
argumentholder extractarguments(string cmd) {
stringtokenizer t = new stringtokenizer(cmd);
int tokencount = t.counttokens();
argumentholder a = new argumentholder();
string token = t.nexttoken();
while(t.hasmoretokens()) {
token = t.nexttoken();
try {
int i = integer.parseint(token);
a.setargument(i);
}
catch (numberformatexception e) {
a.setargument(token);
}
}
return a;
}
//以下6个以meth开头的函数实现本测试程序
接受的合法命令的具体处理过程。
//函数的命名规则为:meth+格式化后的用户命令
public string methadd(int i1, int i2) {
return integer.tostring(i1) + " + " +
integer.tostring(i2) + " = " + integer.tostring(i1 + i2);
}
public string methconcat(string s1, string s2) {
return "the concatenated string is " + s1 + s2;
}
public string methhelp() {
final string[] helpmessages =
{"dynacalltest version 1.0",
"valid commands are:",
"add int1 int2",
"concat string1 string 2",
"help",
"minmax int1 int2 int3",
"quit",
"rand"
};
for (int i = 0; i < helpmessages.length; ++i)
system.out.println(helpmessages[i]);
return "";
}
public string methminmax(int i1, int i2, int i3) {
return ("min = " +
integer.tostring(java.lang.math.min
(java.lang.math.min(
i1,i2),i3)) + ", max = " + integer.tostring(
java.lang.math.max(java.lang.math.max(i1,i2),i3)));
}
public string methquit() {
return "quitting";
}
public string methrand() {
return "the random number is "
+ integer.tostring(rand.nextint());
}
//处理用户在命令行中的输入
//调用extractcommand以生成对应于用户输入的方法名
//调用extractarguments以封装用户输入参数
//调用invoker.dynacall以实现命令处理
string processcommand(string cmd) {
try {
string meth = "meth" + extractcommand(cmd);
argumentholder a = extractarguments(cmd);
return (string)(invoker.dynacall(this, meth,a));
}
catch (nosuchmethodexception e) {
return "no method to process command " + cmd;
}
catch (invocationtargetexception e) {
system.out.println("trace:");
e.printstacktrace();
return "invocationtargetexception
processing command" + cmd;
}
catch (illegalaccessexception e) {
system.out.println("trace:");
e.printstacktrace();
return "illegalaccessexception processing
command" + cmd;
}
}
//主函数,调用processcommand以实现程序功能
public static void main(string args[]) {
boolean allok = true;
string line;
dynacalltest myclient = new dynacalltest();
system.out.println("dynacalltest version 1.0");
system.out.println("enter command at the prompt");
system.out.println("type help for help");
while(allok) {
try {
line = myclient.getcommandline();
if (line == null)
allok = false;
else {
system.out.println(myclient.processcommand(line));
if (line.substring(0,1).touppercase().equals("q"))
allok = false;
}
}
catch (ioexception e) {
e.printstacktrace();
allok = false;
}
}
system.exit(0);
}
}
闽公网安备 35060202000074号