我正在开发一个实用程序类来处理来自Java Swing组件的Actions; 我想知道是否有一种方法可以检查给定的方法名称(将通过反射访问)是否在编译时存在,如果没有则显示编译器错误?
- 更新
好的,看起来我不清楚,让我们谈谈细节:
我有一个名为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的参数,我将在运行时看到它。我想在编译时看到它。得到它了? :)
顺便说一下,这个班级工作得很好,我只是想做这个改进。
- 更新
学习Annotations方法
答案 0 :(得分:14)
听起来好像你想建立一个必须实施特定方法的合同。
在Java中,您通常通过界面执行此操作:
public interface Frobnicator {
public void frobnicate();
}
然后在您的其他代码中,您只需要获得该类型的对象,这样编译器将验证该方法是否存在:
public void frobnicateorize(Frobnicator forb) {
frob.frobnicate();
}
这样你甚至可以避免使用反射来调用方法。
关于更新的编辑:不,您不能让Java编译器静态检查这种代码。您可能必须编写自己的工具来为您进行检查。
答案 1 :(得分:3)
没有。没有办法做到这一点。
答案 2 :(得分:2)
我不知道如何使用任何现有软件来做到这一点。
但是,假设方法名称可以在编译时解析(在这种情况下,您似乎不太可能使用反射),您可以设想创建一个IDE插件来执行此操作。
答案 3 :(得分:2)
您可以使用注释而不是字符串文字来表示调用的操作,然后使用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
}
}
答案 4 :(得分:2)
在编译时无法检查动态方法。但是,有可能在某种程度上重新设计框架类以满足需要。 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);
它比您现有的实现更冗长,但它为您提供了所需的编译时类型安全性。
答案 5 :(得分:0)
可以反思:
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();
}
答案 6 :(得分:0)
使用“常量”
只要它是正确的类型,编译器在传递字符串文字时不会抱怨。
但是,将变量作为参数传递时,必须至少定义变量。
您可以为每个操作定义一些类变量“常量”,并建议调用者使用这些变量。如果变量参数未定义,编译器将给出错误。
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(...);
答案 7 :(得分:0)
我认为你只需要声明一个回调接口来补充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构造函数的调用,以找到第一个参数来检查是否存在相应命名的方法