Search code examples
javaactionlistenercode-maintainability

How to implement and mantain multiple actionListener


Ok, I have one class (let's call it: MenuBarClass) that contain multiple Menu and MenuItem. I whant assign to every MenuItem an actionlistener, but.. instead of doing something like:

menuitem_1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} });
menuitem_2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} });
menuitem_3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} });
// ...
menuitem_N.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} });

I whant my code more mantainable and.. more important.. I don't whant a lots of "if" in one huge ActionListener class like:

public void actionPerformed(ActionEvent e) {
  if (e.getSource().equals(menuitem_1)) {
    //do stuff..
  } else if (e.getSource().equals(menuitem_2)) {
    //do stuff..
  } else ...
}

how can i do this, if it is possible? Can anyone help?


Solution

  • You can reduce verbosity using the reflection API to create a utility method:

    package demo;    
    import java.awt.event.*;
    import java.lang.reflect.*;
    
    public class ListenerProxies {    
      private static final Class<?>[] INTERFACES = { ActionListener.class };
    
      public static ActionListener actionListener(final Object target,
                                                        String method) {
        final Method proxied = method(target, method);
        InvocationHandler handler = new InvocationHandler() {
          @Override
          public Object invoke(Object proxy, Method method, Object[] args)
              throws Throwable {
            ActionEvent event = (ActionEvent) args[0];
            return proxied.invoke(target, event);
          }
        };
        return (ActionListener) Proxy.newProxyInstance(target.getClass()
            .getClassLoader(), INTERFACES, handler);
      }
    
      private static Method method(Object target, String method) {
        try {
          return target.getClass().getMethod(method, ActionEvent.class);
        } catch (NoSuchMethodException e) {
          throw new IllegalStateException(e);
        } catch (SecurityException e) {
          throw new IllegalStateException(e);
        }
      }
    }
    

    This can be utilized like this:

    package demo;
    import static demo.ListenerProxies.actionListener;
    import java.awt.event.ActionEvent;
    import javax.swing.*;
    
    public class Demo {
    
      public static void main(String[] args) {
        Demo test = new Demo();
        JButton hello = new JButton("Say Hello");
        hello.addActionListener(actionListener(test, "sayHello"));
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(hello);
        frame.pack();
        frame.setVisible(true);
      }
    
      public void sayHello(ActionEvent event) {
        System.out.println("Hello");
      }
    }
    

    The downside of this is the lack of compile-time checking that the sayHello(ActionEvent) method exists.

    The performance costs will be negligible.