从Java有点新的非常简单的问题:当向控件添加事件处理程序(或者在Java中调用的任何东西)时,它必须是一个对象吗?我的意思是,在C#中我可以做到
control.event += System.eventHandler(methodThatHandlesEvent)
当然,那是因为我们在C#中有委托类型,但我想知道我是否可以选择在Java中引发事件时调用哪个方法?我的意思是,在Java中我可以有类似的东西:
control.AddActionListener(objectWhichClassImplementsActionListener)
然后我在这个类中有actionPerformed
方法,这是唯一被调用的方法。我知道有更多种类的监听器,但我不能有一个“ActionListenerHandler”类,我在其中实现了几个可以分配给不同控件的actionPerformed
方法?
答案 0 :(得分:4)
Java没有“方法指针”或函数类型或类似的东西,所以你必须使用ActionListener
接口及其actionPerformed
方法,是的。在大多数情况下,我在那里给出一个匿名类的对象,它只调用处理它的方法:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
myRealMethod();
}
});
与C#版本相比,这有点冗长,但Java从未成为保存代码行的语言。
如果您希望缩短一点,可以使用reflection来帮助。
package de.fencing_game.paul.examples;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import javax.swing.*;
/**
* An adapter class for calling an (almost) arbitrary
* method as an ActionListener.
*<p>
* In fact, you can use any public method which has either no parameter
* or any parameter of type {@link ActionEvent} or some superclass
* of it (then it gets passed the ActionEvent there).
*</p>
* <p>
* Additionally, the adapter needs an object of a class containing
* this method as the receiver.
* </p>
*<p>
* Inspired by the question <a href="http://stackoverflow.com/questions/5182558/java-vs-c-addactionlistener-vs-event-subscription">Java vs C# - AddActionListener vs event subscription</a> on Stackoverflow.
*</p>
* @author Paŭlo Ebermann
*/
public class MethodAdapter implements ActionListener {
/** the receiver object. */
private Object target;
/** the method to be invoked. */
private Method method;
/** true if the method accepts the ActionEvent argument,
false if it is a no-arg method. */
private boolean takesArgument;
/**
* creates a new MethodAdapter.
* @param o the receiver object.
* @param mName the name of a method on the receiver.
* If there are multiple same-named methods in the class
* of the receiver, we take the first one of these: <ul>
* <li> a public method taking an ActionEvent as parameter</li>
* <li> a public method taking an AWTEvent as parameter</li>
* <li> a public method taking an EventObject as parameter</li>
* <li> a public method taking an Object as parameter</li>
* <li> a public method taking no parameter.</li>
* </ul>
* @throws IllegalArgumentException if there is no such method.
*/
public MethodAdapter(Object o, String mName) {
this(o, findMethod(o, mName));
}
/**
* creates a new MethodAdapter.
* @param o the receiver object.
* @param m the method to be invoked.
* This method has to be declared in the class of the receiver object
* or some supertype, has to take no or one argument, and if one, then
* of some supertype of {@link ActionEvent}.
* @throws IllegalArgumentException if the method does not fit the
* receiver, if the method takes too much arguments or arguments of
* wrong types.
*/
public MethodAdapter(Object o, Method m) {
Class<?>[] params = m.getParameterTypes();
if(!m.getDeclaringClass().isInstance(o)) {
throw new IllegalArgumentException("wrong target object");
}
if(params.length > 1) {
throw new IllegalArgumentException("too many arguments");
}
if(params.length == 1 &&
! params[0].isAssignableFrom(ActionEvent.class)) {
throw new IllegalArgumentException("method not compatible: " + m);
}
this.target = o;
this.method = m;
this.takesArgument = params.length > 0;
}
private static Method findMethod(Object o, String mName) {
Class<?> c = o.getClass();
Class<?> eventClass = ActionEvent.class;
while(eventClass != null) {
try {
return c.getMethod(mName, ActionEvent.class);
}
catch(NoSuchMethodException ex) {}
eventClass = eventClass.getSuperclass();
}
try {
// try a no-argument method
return c.getMethod(mName);
}
catch(NoSuchMethodException ex) {
throw new IllegalArgumentException("No fitting method named '" +
mName +"' on this object " + o +
" of class " + c.getName());
}
}
/**
* the implementation of the actionPerformed method.
* We delegate to our target object and method.
* Any return value of
* the method is silently ignored.
* @throws RuntimeException if any exception is thrown by the invoked
* method (or during the invoke process), it is wrapped in a
* RuntimeException and rethrown.
*/
public void actionPerformed(ActionEvent event) {
try {
if(takesArgument) {
method.invoke(target, event);
}
else {
method.invoke(target);
}
}
catch(Exception e) {
if(e instanceof InvocationTargetException) {
Throwable t = e.getCause();
if(t instanceof Error)
throw (Error)t;
e = (Exception)t;
}
if(e instanceof RuntimeException)
throw (RuntimeException) e;
throw new RuntimeException(e);
}
}
/**
* main method for testing purposes.
*/
public static void main(String[] params) {
JFrame f = new JFrame("Test");
JButton b = new JButton("close");
b.addActionListener(new MethodAdapter(f, "dispose"));
f.getContentPane().add(b);
f.setVisible(true);
}
}
有了这门课,你可以写
button.addActionListener(new MethodAdapter(this, "myRealMethod"));
代替上面的代码,并在按钮上单击该方法将被调用。 请注意,现在编译器无法进行任何类型检查,因此必须在运行时完成(并且您应该检查System.err是否有异常堆栈跟踪 - 即使某些事件侦听器抛出异常,应用程序也将继续运行)。它也会慢一些,但对于用户交互来说,这并不重要。
现在这也是我github repository的一部分。
更新:使用Java 8,有方法引用表达式(如this::myRealMethod
)。 Java仍然没有函数类型,因此这些表达式只有在需要SAM接口(单个抽象方法)的情况下才有可能,然后被包装到该接口的编译器生成的实现中,该实现包括目标对象引用和实现调用目标方法的接口方法。
答案 1 :(得分:1)
它必须是一个物体吗?
在Java中,所有应该是 Object) 您可以根据需要使用任意数量的参数声明侦听器,但 Observable 对象应该知道调用什么方法和传递什么参数。
答案 2 :(得分:1)
您可以使用匿名内部类来避免单独声明ActionListener
并使用不必要的帮助程序类弄乱您的项目:
control.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked.");
}
});
注意new ActionListener() { ... }
,它声明了一个实现ActionListener
的匿名类。
答案 3 :(得分:1)
创建一个封装子类的类,如
public class MyEvents
{
public class MyEvent_1 : ActionListner
{
....
}
public class MyEvent_2 : ActionListner
{
....
}
}
这样你所有的活动都会在你想要的同一个地方。