Java与C# - AddActionListener与事件订阅

时间:2011-03-03 15:08:52

标签: c# java

从Java有点新的非常简单的问题:当向控件添加事件处理程序(或者在Java中调用的任何东西)时,它必须是一个对象吗?我的意思是,在C#中我可以做到

control.event += System.eventHandler(methodThatHandlesEvent)

当然,那是因为我们在C#中有委托类型,但我想知道我是否可以选择在Java中引发事件时调用哪个方法?我的意思是,在Java中我可以有类似的东西:

control.AddActionListener(objectWhichClassImplementsActionListener)

然后我在这个类中有actionPerformed方法,这是唯一被调用的方法。我知道有更多种类的监听器,但我不能有一个“ActionListenerHandler”类,我在其中实现了几个可以分配给不同控件的actionPerformed方法?

4 个答案:

答案 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
   {
    ....
   }
}

这样你所有的活动都会在你想要的同一个地方。