Java: обрабатывать подклассы равномерно - PullRequest
2 голосов
/ 23 ноября 2011

Предположим, у меня есть это:

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 это можно было бы сделать с помощью массива структур с двумя элементами: строкой и указателем на функцию).

Ответы [ 5 ]

5 голосов
/ 23 ноября 2011

Только 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):

Command[] commands = {new Slice(), new Dice()};
for (Command cmd : commands) {
  if (some_string.equals(cmd.getClass().getName())) {
    cmd.run (argv);
    return;
  }
}
2 голосов
/ 23 ноября 2011

Я настоятельно рекомендую вам просто отбросить рефлексию и создать экземпляры классов (чтобы методы перестали быть статичными), все будет намного яснее и понятнее, и позже вы обнаружите, что этот дизайн позволяет улучшить ваше классы - если вы продолжите идти по этому пути, вы попадете в мир боли (ну, по крайней мере, маленький город боли)

Мне нравится, что Reflection нетривиален в Java именно по этой причине, он немного отговаривает людей и заставляет их переосмыслить свой подход.

Обратите внимание, что вы также привязываете некоторые данные непосредственно к имени класса (конструкция кода). Я не говорю, что это неправильно, я делал это много раз, и это чрезвычайно заманчиво и элегантно - но в конце концов я всегда сожалел об этом в той или иной степени. Появляется некоторая реальная проблема, которая не соответствует вашим соглашениям / правилам именования классов ...

Я бы просто использовал такой шаблон вместо этого: Command.wants (some_string), либо тот, либо карта сопоставления экземпляра строки-класса, которая удаляет весь ваш цикл, и код становится

myHashMap<Command>.get(some_string).run();
1 голос
/ 23 ноября 2011

Попробуйте объявить массив команд как Class<? extends Command>

также, как показано здесь, Command должна быть интерфейсом, а не абстрактным классом (если вы не планируете добавить больше поведения к нему позже).

0 голосов
/ 23 ноября 2011
Class<Command>[] commands = {Slice.class,Dice.class}; /* compile error */

Вы не можете создать универсальный массив в Java.

Итак, вы должны использовать некоторый класс из фреймворка Collection, если вам нужен универсальный сборник.

, например

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});    
}
0 голосов
/ 23 ноября 2011

Ваш дизайн - плохой способ реализовать этот шаблон в любом случае.В 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 от имени команды до реализации вместо поиска в массиве.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...