Hi guys,
with event handlings it seems that there are 2 approaches:
1)using an anonymous inner class
2)using a normal inner class.
From what I can see if you have more than one component that is generating an event, say you have 3 buttons, if you go down the route of anonymous inner classes then you need to have effectively 3 inner classes, one for each component. If you instead use a normal - I call it normal, not sure what's the right name for it - inner class then you could techincally use only one.
So, what's better? My understanding is that anonymous inner classes are more widely used because there is less code to write, that's if you use 1, but that might not be the case. What do you guys say? I'm writing a little simple application with three buttons, and I'm thinking not to use an anonymous inner class.
thanks

Edited 1 Year Ago by pritaeas: Moved to software.

The AIC approach gives you a separate handler for each event, which is typically what you want. You use a "normal" inner class when you want to pass parameters into the handler when setting it up - that's something you do when you create dynamiclally changing layouts for example. Using one handler for three buttons is a mess because you just end up with a handler that's one big switch (unless the buttons are very similar, eg the number buttons in a calulator).

However, we are now well into the Java 8 era, so the current answer is "neither", just use a lamba.
If the handler is a short piece of code it looks like

myButton.addActionListener( e -> System.out.println("Action event " + e) );

If it's longer, put it in a method and reference it like:

void myHandler(ActionEvent e) {
   // etc
}
...
myButton.addActionListener( this::myHandler );

Isn't that easier and cleaner?

Edited 1 Year Ago by JamesCherrill

thanks, but I'm stuck with java 7 here, company policy I believe. Will stick to AIC for now then.
The lambda expression seems interesting though

something that might make a difference between an anonymous inner class, and a named one is, do you plan to use it for several components? if so, it would be crazy to re-write the anonymous inner classes each time you want to use it, if you can just re-use an existing named class.

Agreed, provided the reason for using for several components is that there is a lot of commonality between them (see example in previous post!). In beginners code we often see a single handler used for unrealeted actions, eg Save, Cancel, OK, Undo etc, so you get the horrible long switch (or even worse an if / else if / else if / ... mess).

Well yes I am. Here is the example. I've built this small application as mentioned before, (a text area where users can type and two buttons, (the increase button increases the size of the text and the decrease button decreases it). Since each button hasa different functionality I used 2 anonymous classes,
Here is the ChangeFont.java file

/*ChangeFont.java*/
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
//import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JTextArea;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Font;
public class ChangeFont extends JFrame{
    //private JPanel panel;
    private JButton increment;
    private JButton decrement;
    private JTextArea textArea;
    private BorderLayout borderLayout;  
    private Font font;
    private int fontQuota;


    public ChangeFont(){//constructor
        super("Change font size application");
        //panel new JPanel();
        fontQuota = 9;
        textArea = new JTextArea(10,15);
        increment = new JButton("Increment");
        decrement = new JButton("Decrement");       
        borderLayout = new BorderLayout();
        setLayout(borderLayout);
        add(textArea, BorderLayout.CENTER);
        add(increment, BorderLayout.EAST);
        add(decrement, BorderLayout.WEST);
        font = new Font("Serif", Font.PLAIN,fontQuota);//set new font to serif, plain 9px size
        textArea.setFont(font);//set the new font



        //increment event handling
        increment.addActionListener(
            new ActionListener(){
                public void actionPerformed(ActionEvent event){
                    textArea.setFont(new Font("Serif", Font.PLAIN, ++fontQuota));
                }
            }
        );
        //decrement event handling
        decrement.addActionListener(
            new ActionListener(){
                public void actionPerformed(ActionEvent event){
                    textArea.setFont(new Font("Serif", Font.PLAIN, --fontQuota));
                }
            }
        );

    }

}//end of ChangeFont class

And here is how it looks like
font-size-app.png

Well, for this situation, this is perfect. There's no need in writing additional code if it's of no use to you.
My example was rather on, let's say we have 10 inputFields, and they all should perform the same task when a user types in it.
Then, you can create a named inner class, and for each inputField, you just say:

inputField.addHandler(new MyNamedClass());

instead of x times:

inputField.addHandler(new Handler(){
  @Override
  public void handleEvent(Event event){
     // the code
  }
});

A third option, which you could use, is having the class itself implement the Listener:

public class ChangeFont extends JFrame{

becomes

public class ChangeFont extends JFrame implements ActionListener{

Now, the class itself must provide an implementation of actionPerformed. Based on the event passed as parameter, you can deduce which button triggered the event:

public void actionPerformed(ActionEvent event){
                    if ( event.getSource().equals(decrement )){
                    textArea.setFont(new Font("Serif", Font.PLAIN, --fontQuota));
                    }
                    else if ( event.getSource().equals(increment)){
                    textArea.setFont(new Font("Serif", Font.PLAIN, ++fontQuota));
                    }
                }

to add the listener to the buttons, you can do just:

decrement.addActionListener(this);
increment.addActionListener(this);

I just hate those if else if handlers. How about a reusaable parametised inner class

class FontResizer implements ActionListener {
   int delta;
   FontResizer(int delta) {
       this.delta = delta;
    }
    public void actionPerformed(ActionEvent event){
        fontQuota += delta;
        textArea.setFont(new Font("Serif", Font.PLAIN, fontQuota) ;   
    }
}

then its a nicely readable

decrement.addActionListener(new FontResizer(-2));
increment.addActionListener(new FontResizer(2));

... and it scales beautifully

Edited 1 Year Ago by JamesCherrill

cool, the "this" keyword often confuse me a little I have to admit. For the purpose of the exercise I left it as it was, as, please do correct me if I'm wrong, I seem to understand that the way I've implemented is correct

Especially for code as limited as the one you're working on, it's not a problem.
But, as James showed, you can easily improve your code (make it reusable and parameterizable) with little effort.

Sure, I don't mind doing that, but then we remove the anonymous class and replace it with a named one. I'll give it a go

OK, sorry, now that I've done it, I can see the real benefits :-), thanks.
I've also made some changes, added scrollbars when needed (when you increment the font size the textarea grows till it overlaps the buttons, but with the scrollbar this doesn't happen), so here is the final code, with the parametized class added to it and a gridLayout for the JPanel (I've also added a JPanel, why not) and a borderLayout for the JFrame. All compiled and working.
font-size-app_1.png

/*ChangeFont.java*/
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JTextArea;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Font;
import javax.swing.JScrollPane;
public class ChangeFont extends JFrame{
    private JPanel panel;
    private JButton increment;
    private JButton decrement;
    private JTextArea textArea;
    private BorderLayout borderLayout;  
    private GridLayout gridLayout;
    private Font font;
    private int fontQuota;


    public ChangeFont(){//constructor
        super("Change font size application");
        panel = new JPanel();
        gridLayout = new GridLayout(1,2,10,0);//create new gridbag 1 row and 2 cols 
        fontQuota = 9;
        textArea = new JTextArea(5,15);
        increment = new JButton("Increment");
        decrement = new JButton("Decrement");       
        borderLayout = new BorderLayout();
        panel.setLayout(gridLayout);//set layout of panel

        setLayout(borderLayout);//set layout of JFrame      
        panel.add(increment);//add to the JPanel
        panel.add(decrement);//add to the JPanel
        add(new JScrollPane(textArea), BorderLayout.NORTH);//add scrollbar if necessary and add textarea to JFrame directly
        add(panel, BorderLayout.SOUTH);//add JPanel to JFrame
        font = new Font("Serif", Font.PLAIN,fontQuota);//set new font to serif, plain 9px size
        textArea.setFont(font);//set the new font
        //event handling code
        decrement.addActionListener(new FontResizer(-2));
        increment.addActionListener(new FontResizer(2));


    }//end of constructor
    private class FontResizer implements ActionListener{//named class
        int delta;//increment/decrement to be added to fontQuota
        public FontResizer(int delta){
            this.delta = delta;
        }
        public void actionPerformed(ActionEvent event){
            fontQuota += delta;
            textArea.setFont(new Font("Serif", Font.PLAIN, fontQuota));  
        }
    }

}//end of ChangeFont class




/*ChangeFontTest.java*/
import javax.swing.JFrame;
public class ChangeFontTest{
    public static void main(String[] args){
        ChangeFont changeFont = new ChangeFont();
        changeFont.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        changeFont.setSize(600, 200);
        changeFont.setVisible( true );
    }
}

Edited 1 Year Ago by Violet_82

This article has been dead for over six months. Start a new discussion instead.