javaooplisteners

Is there a better practice for Listeners?


Say I have a swing GUI, and I want to listen MouseEvents. Who do you think should be the Listener class, who should be responsible? What is the best-or preferred way to implement it? Any opinions? I usually go like this:

public class MyPanel extends JPanel implements MouseListener{
    private JTable table;
    public void foo(){
         table.addMouseListener(this);
    }
    /* MouseListener */
    //Implement MouseListener here.
}

Is there a better way?


EDIT: Thank you everyone for the wisdom and help. I appreciate it.


Solution

  • There are a few common ways to do event listeners (the only one that I can think of that I left off in the code below is static inner classes). The code below uses ActionListener since it is simplest, but you can apply the idea to any listener.

    Notice that the "this" way (having the class implement the listener) can lead to a huge if/else set of statements. I'd say this is the worst possible way because of that. I dislike having "clearing house" methods for two reasons:

    1) they are large 2) it is tempting to do the work inside of the method rather than having each if/else call a method to do the work (which, as you can see, is what I did here... oops :-)

    I also do not like the Anonymous way for two reasons:

    1) you cannot easily re-use the code so you may find that you have duplicate code after a while 2) I find it breaks up the reading of the code (others disagree... personal taste). I think everyone would agree that if you are doing more than 5-10 lines that an anonymous inner class is not a good idea (I would say more than 2 is too much).

    That leaves the inner and the outer ways. I would use the outer way when I am writing a listener that is not tightly tied to the GUI that it is listening. If the listener doesn't need information (member variables/methods) that are part of the class (TestFrame in this case) I would go for the outer class. In the example below I passed in the "this" so that the outer listeners could access the GUI... if I were to write code like that I would instead make it an inner class since it requires something from the GUI.

    So, my order of preference is:

    And here is the code

    import java.awt.FlowLayout;
    import java.awt.Rectangle;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    
    
    public class Main
    {
        public static void main(String[] args)
        {
            SwingUtilities.invokeLater(
                new Runnable() 
                {
                    public void run() 
                    {
                        createAndShowGUI();
                    }
                });
        }
    
        private static void createAndShowGUI()
        {
            final TestFrame frame;
    
            frame = new TestFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setBounds(new Rectangle(10, 10, 300, 300));
            frame.init();
            frame.setVisible(true);
        }
    }
    
    class TestFrame
        extends    JFrame
        implements ActionListener
    {
        private final JButton aBtn;
        private final JButton bBtn;
    
        public TestFrame()
        {
            super("Test");
    
            aBtn = new JButton("A");
            bBtn = new JButton("B");
        }
    
        public void init()
        {
            setLayout(new FlowLayout());
            add(aBtn);
            add(bBtn);
    
            // the class, since it implements ActionListener
            aBtn.addActionListener(this);
            bBtn.addActionListener(this);
    
            // outer classes
            aBtn.addActionListener(new OuterAListener(this));
            bBtn.addActionListener(new OuterBListener(this));
    
            // inner class
            aBtn.addActionListener(new InnerAListener());
            bBtn.addActionListener(new InnerBListener());
    
            // anonymous classes
            aBtn.addActionListener(
                new ActionListener()
                {
                    public void actionPerformed(final ActionEvent e)
                    {
                        System.out.println ("Hi from Anonymous A");
                    }
                });
    
            bBtn.addActionListener(
                new ActionListener()
                {
                    public void actionPerformed(final ActionEvent e)
                    {
                        System.out.println ("Hi from Anonymous B");
                    }
                });
        }
    
        public void actionPerformed(final ActionEvent evt)
        {
            final Object source;
    
            source = evt.getSource();
    
            if(source == aBtn)
            {
                System.out.println ("Hi from this A");
            }
            else if (source == bBtn)
            {
                System.out.println ("Hi from this B");
            }
            else
            {
                // ???
            }
        }
    
        private class InnerAListener
            implements ActionListener
        {
            public void actionPerformed(final ActionEvent e)
            {
                System.out.println ("Hi from Inner A");
            }
        }
    
        private class InnerBListener
            implements ActionListener
        {
            public void actionPerformed(final ActionEvent e)
            {
                System.out.println ("Hi from Inner B");
            }
        }
    }
    
    class OuterAListener
        implements ActionListener
    {
        private final TestFrame frame;
    
        public OuterAListener(final TestFrame f)
        {
            frame = f;
        }
    
        public void actionPerformed(final ActionEvent e)
        {
            System.out.println ("Hi from Outer A");
        }
    }
    
    class OuterBListener
        implements ActionListener
    {
        private final TestFrame frame;
    
        public OuterBListener(final TestFrame f)
        {
            frame = f;
        }
    
        public void actionPerformed(final ActionEvent e)
        {
            System.out.println ("Hi from Outer B");
        }
    }