|
相 信 有 不 少 人 使 用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); } }
|