java:统一处理子类

时间:2011-11-22 20:16:15

标签: java generics reflection

假设我有这个:

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.classDice.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中,这可以通过一个包含两个元素的结构数组来完成:一个字符串和一个指向函数的指针。)

5 个答案:

答案 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});    
}