Java: Проверьте, существует ли данное имя метода во время компиляции

Я разрабатываю служебный класс для обработки Действий от Java компоненты Swing; я хотел бы знать, существует ли способ проверить, существует ли данное имя метода (к которому получат доступ отражения) во время компиляции, и покажите ошибку компилятора если нет?

- обновление

Хорошо, похож, я не был ясен, позволяет разговору о деталях:

У меня есть класс под названием TomAction, который я использую для упрощения простых объявлений действий в моем проекте. Вместо записи что-то как:

class MyClass {

    public MyClass() {
    Icon icon = null; // putting null to simplify the example
    JButton btn = new JButton(new AbstractAction("Click to go!", icon) {
        public void actionPerformed(ActionEvent ev) {
        try {
            go();
        } catch (Exception e) {
            e.printStackTrace();
            String msg = "Fail to execute 'go' task.";
            JOptionPane.showMessageDialog(null, msg, "Fail", JOptionPane.ERROR_MESSAGE);
        }
        }
    });
    }

    private void go() {
    // do task
    }

}

.. Я просто пишу:

class MyClass {

    public MyClass() {
    String msg = "Fail to execute 'go' task.";
    Icon icon = null; // putting null to simplify the example
    TomAction act = new TomAction("go", this, "Click to go!", msg, icon);
    JButton btn = new JButton(act);
    }

    private void go() {
    // do task
    }

}

.. и программа имеет то же поведение.

Проблема состоит в том, что, если я ввожу неправильное имя метода как аргумент TomAction, я буду видеть его только во времени выполнения. Я хотел бы видеть его во время компиляции. Получил его?:)

Между прочим, этот класс работает очень прекрасный, я просто хочу сделать это улучшение.

- обновление

изучение подхода Аннотаций

5
задан wattostudios 6 May 2012 в 03:46
поделиться

8 ответов

Вы можете использовать аннотацию вместо строкового литерала для обозначения вызванного действия, а затем использовать APT для проверки существования метода. APT вызывается автоматически javac.

http://java.sun.com/javase/6/docs/technotes/tools/windows/javac.html#processing http://java.sun.com/j2se/1.5.0/docs /guide/apt/GettingStarted.html http://java.sun.com/j2se/1.5.0/docs/guide/apt/mirror/overview-summary.html

Используя ваш пример, я бы Предложите дизайн следующим образом: вы помещаете привязку аннотации в код и добавляете код создания экземпляра действия в обработчик аннотаций. Действия в этом случае должны быть членами класса, что в любом случае является хорошей практикой. Убедитесь, что вы используете пакеты ресурсов для значков и текста, и в конечном итоге вы сэкономите много горя:

class MyClass {
    // the keys should be looked up from resource bundle
    @TomActionBinding("go", "text-key", "msg-key", "icon-key");
    Action act;

    public MyClass() {
       JButton btn = new JButton(act);
    }

    private void go() {
       // do task
    }

}
2
ответ дан 18 December 2019 в 06:49
поделиться

Это возможно с отражением:

        Object o = object;
        try {
            Method m = o.getClass().getMethod("methodName");
            m.invoke(o);
        } catch (NoSuchMethodException e) {
            //Method is not available
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
0
ответ дан 18 December 2019 в 06:49
поделиться

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

В Java вы обычно делаете это через интерфейс:

public interface Frobnicator {
  public void frobnicate();
}

Затем в вашем другом коде вы просто ожидаете получить объект этого типа, таким образом компилятор проверит, что метод существует:

public void frobnicateorize(Frobnicator forb) {
  frob.frobnicate();
}

Таким образом вы можно даже избежать использования отражения для вызова метода.

Отредактируйте относительно обновления: Нет, компилятор Java не может статически проверять такой код. Возможно, вам придется написать свой собственный инструмент, чтобы сделать эту проверку за вас.

14
ответ дан 18 December 2019 в 06:49
поделиться

Нет. Нет никакого способа сделать это.

3
ответ дан 18 December 2019 в 06:49
поделиться

Я не знаю, как сделать это с помощью какого-либо существующего программного обеспечения.

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

2
ответ дан 18 December 2019 в 06:49
поделиться

Используйте «константы»

Компилятор не будет жаловаться на передачу строкового литерала, если это правильный тип.

Однако при передаче переменной в качестве параметра переменная должна быть как минимум определена.

Вы можете определить некоторые «константы» переменных класса для каждого действия и предложить вызывающему объекту использовать их. Компилятор выдаст вам ошибку, если параметр переменной не определен.

public class TomAction {
  public static final String GO_ACTION = "go";

  ...
}

Чтобы вызвать его, вместо этого:

TomAction act = new TomAction("go", ...);

Сделайте следующее:

TomAction act = new TomAction(TomAction.GO_ACTION, ...);

Если вы попытаетесь использовать неопределенную переменную:

TomAction act = new TomAction(TomAction.WRONG_ACTION, ...);

Вы получите сообщение об ошибке:

TestClass.WRONG_CONSTANT cannot be resolved

Используйте типы объектов

Чтобы расширить идею передачи переменных, почему бы не передавать объекты и требовать, чтобы объект был правильного типа? Таким образом, они больше не передают String, поэтому они абсолютно не могут передавать что-то, что не определено.

public class ActionType {
  public String action;
}

public class GoAction extends ActionType {
  public GoAction() {
    this.action = "go";
  }
} 

public class TomAction {

  public TomAction(ActionType actionType, ...) {
    // Use actionType.action
    ...
  }
  ...

}

Чтобы вызвать это, вы делаете следующее:

TomAction act = new TomAction(new GoAction(), ...);

Конечно, это приводит нас к ...

Расширить класс

Расширить класс вместо использования параметра.

public class GoAction extends TomAction {
}

Затем выполните следующее:

TomAction act = new GoAction(...);
0
ответ дан 18 December 2019 в 06:49
поделиться

Нет способа чтобы проверить наличие динамического метода во время компиляции. Однако, возможно, удастся несколько изменить дизайн вашего каркасного класса, чтобы он делал то, что вам нужно. Единственная функциональность, которую ваш класс TomAction действительно предлагает за пределами AbstractAction , - это обработка ошибок и возможность переименовать метод actionPerformed при (высокой, ИМХО) стоимости использования отражение и потеря безопасности типов.

Я бы реализовал это следующим образом:

public class TomAction extends AbstractAction {

    public static interface Command {
        // CommandException is implemented elsewhere
        public void execute() throws CommandException;
    }

    private Command cmd;
    // other members omitted for brevity

    public TomAction(Command cmd, String name, String failMsg, Icon icon) {
        super(name, icon);
        this.cmd = cmd;
        this.failMsg = failMsg;
    }

    public void actionPerformed(ActionEvent e) {
        try {
            cmd.execute();
        }
        catch (CommandException e) {
            // handle failure
        }
    }

    // remaining implementation
}

Затем, чтобы использовать:

TomAction.Command goCmd = new TomAction.Command() {
    public void execute() throws CommandException {
        go();
    }
};

TomAction act = new TomAction(goCmd, "Click to go!", msg, icon);
JButton btn = new JButton(act);

Он немного более подробный, чем ваша существующая реализация, но дает вам желаемую безопасность типов во время компиляции.

2
ответ дан 18 December 2019 в 06:49
поделиться

Я думаю, вам просто нужно объявить интерфейс обратного вызова, чтобы дополнить ваш класс TomAction как часть вашего "фреймворка" TomAction:

interface TACB { // TomActionCallback
    void go();
}

Тогда ваш пример использования станет:

class MyClass implements TACB {

    public MyClass() {
        String msg = "Fail to execure 'go' task.";
        Icon icon = null; // putting null to simplify the example
        TomAction act = new TomAction(this, this, "Click to go!", msg, icon);
        JButton btn = new JButton(act);
    }

    public void go() {
        // do task
    }

}

Альтернативно:

class MyClass {

    public MyClass() {
        String msg = "Fail to execure 'go' task.";
        Icon icon = null; // putting null to simplify the example
        TACB tacb1 = new TACB() { public void go() { this.go1() } };
        TomAction act1 = new TomAction(tacb1, this, "Click to go!", msg, icon);
        JButton btn1 = new JButton(act1);
        TACB tacb2 = new TACB() { public void go() { this.go2() } };
        TomAction act2 = new TomAction(tacb2, this, "Click to go!", msg, icon);
        JButton btn2 = new JButton(act2);
    }

    private void go1() {
        // do task
    }

    private void go2() {
        // do task
    }

}

Да, это немного сложнее, но это стоимость проверки компилятора. Если вам все еще не нравится это решение, единственное, что я могу придумать, - это написать свой собственный инструмент, который сканирует файлы классов и анализирует вызовы конструктора TomAction, чтобы найти первый аргумент для проверки существования метода с соответствующим именем. .

0
ответ дан 18 December 2019 в 06:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: