假设我有这个:
abstract class Command {
static void run (String[]argv) {}
}
class Slice extends Command {
static void run (String[]argv) {/* slicing code */}
}
class Dice extends Command {
static void run (String[]argv) {/* dicing code */}
}
我希望能够遍历这些命令,所以我写道:
Class<Command>[] commands = {Slice.class,Dice.class}; /* compile error */
for (Class<Command> c : commands) {
if (some_string.equals(c.getName()) {
c.getDeclaredMethod("run",String[].class).invoke(argv);
return;
}
}
但是,Slice.class
和Dice.class
似乎无法与Class<Command>
放在同一个容器中。
如果我将Class<Command>
替换为Class
,代码会编译一个警告:
[unchecked] unchecked call to getDeclaredMethod(java.lang.String,java.lang.Class<?>...) as a member of the raw type java.lang.Class
c.getDeclaredMethod("run",String[].class).invoke(argv);
当然可以被压制,但我想知道,有更优雅的解决方案吗?
(当然,在C中,这可以通过一个包含两个元素的结构数组来完成:一个字符串和一个指向函数的指针。)
答案 0 :(得分:5)
只有Command.class
属于Class<Command>
类型。它的所有子类型都是Class<? extends Command>
类型。如果将数组和迭代器类型更改为Class<? extends Command>
,则其余代码应该可以正常工作。
Class<? extends Command>[] commands = {Slice.class,Dice.class};
for (Class<? extends Command> c : commands) {
if (some_string.equals(c.getName()) {
c.getDeclaredMethod("run",String[].class).invoke(argv);
return;
}
}
除非你需要反射,否则简单地拥有一个Command对象数组(需要删除run
方法上的static关键字)会更简单:
Command[] commands = {new Slice(), new Dice()};
for (Command cmd : commands) {
if (some_string.equals(cmd.getClass().getName())) {
cmd.run (argv);
return;
}
}
答案 1 :(得分:2)
我强烈建议你放弃反射并制作你的类实例(这样方法不再是静态的),一切都会更清晰,更明智,你后来会发现这个设计可以让你今后改进课程 - 如果你沿着这条路走下去,你就会陷入一个受伤的世界(好吧,至少是一个受伤的小城市)
我喜欢,因为这个原因,反思在Java中是非平凡的,它会使人们稍微劝阻并让他们重新思考他们的方法。
请注意,您还将一些数据直接绑定到类名(代码构造)。我并不是说这是错的,我已经做了很多次,而且非常诱人和优雅 - 但最终我总是在某种程度上后悔。一些现实世界关注的问题与你的类命名约定/规则不符......
我只会使用这样的模式:Command.wants(some_string),或者有一个字符串到类的实例映射,它会删除整个循环并且代码变为
myHashMap<Command>.get(some_string).run();
答案 2 :(得分:1)
尝试将命令数组声明为Class<? extends Command>
此外,在这里显示的方式,Command应该是一个接口,而不是一个抽象类(除非你打算稍后向它添加更多的行为)。
答案 3 :(得分:0)
无论如何,您的设计是实施此模式的不好方法。在Java中,如果您使用实例,即使您不需要实例状态,也可以获得更清晰的代码:
interface Command {
void run(String[] args);
}
class Slice implements Command {
void run(String[] args) { /* slices */ }
}
class Dice implements Command {
void run(String[] args) { /* dices */ }
}
Command[] = {new Slice(), new Dice()};
for (Command c : commands) {
if (someString.equals(c.getClass().getName())) {
c.run(args);
return;
}
}
(我也可能将HashMap
中的命令从命令名存储到实现中,而不是搜索数组。)
答案 4 :(得分:0)
Class<Command>[] commands = {Slice.class,Dice.class}; /* compile error */
您无法在Java中创建通用数组。
因此,如果需要泛型集合,则必须使用Collection框架中的某些类。
e.g。
List<Class<? extends Command>> commandList = new ArrayList<Class<? extends Command>>();
commandList.add(Slice.class);
commandList.add(Dice.class);
for(Class<? extends Command> command : commandList) {
Method runMethod = command.getDeclaredMethod("run", String[].class);
runMethod.setAccessible(true);
runMethod.invoke(null, new Object[]{null});
}