9

Some time ago I wrote a little image viewer/processing program with Java, a mini-Photoshop, if you will.

I wanted there to be a drop-down menu where I could select which one of the images I have opened would be "on the table", ie. shown and methods applied to. I wanted the name of the image to be the name of the JMenuItem shown in the menu. I also wanted a new button to appear when I add a new image.

I wondered this for some time and finally produced this solution, a new class that handles the creation of the new button when an image is added. The code is as follows:

import java.awt.event.*;
import javax.swing.*;
import java.util.*;


public class ImageList{

    private ArrayList<JMenuItem> list;
    private ImageHandler main;
    private ImageLevel img;

    public ImageList() {}

    public void setHandler(ImageHandler hand) {
        main = hand;
        img = main.getImg1();
    }

    public void add(Buffer addi) {
        final String added = addi.getName();
        JMenuItem uusi = new JMenuItem(added);

        main.getMenu5().add(uusi);
        uusi.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                img.setBuffer(added);
                main.getScr().updateUI();
            }
        });
   }
}

This works as it should. For this site I translated the original Finnish names to English, wonder why I wrote them in Finnish originally...I suck at naming things.

Method add is supposed to be called multiple times while the program is running.

What I cannot understand really is the inner class implementation of the interface ActionListener, namely its compilation and how it works.

If I have two buttons on my interface and I want them to do different things, I need two inner classes, one for each, each having its own inner implementation of the interface ActionListener. But in my code there is one class that seems to do the work of many, one complied .class-file for it, but the final result works as if there were many.

Can someone educate me on this issue? Is this code here one class and new buttons are instances of it? Are they new classes? Should there be a new .class-file for each new button? etc...

Valtteri
  • 463
  • 1
  • 5
  • 11
  • 3
    Re. naming: `new` is a reserved word so you should use something different for that JMenuItem variable. – assylias Nov 16 '12 at 16:05
  • Also consider `Action`, as shown in this [`FileMenu`](http://stackoverflow.com/a/4039359/230513) – trashgod Nov 16 '12 at 20:03

6 Answers6

5

Often, an inner class is instantiated in a code which is only called once (e.g. when you extend JPanel and add ActionListeners to JButtons in the constructor). Here, you instantiate an inner class in a method which you call several times (if I understand your description correctly). Each time you call add(), a new instance of the inner class will be created. As with named classes, there is only one class, but there may be many instances.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
  • @Code-Guru: OK, so they are new instances. Never really seen, or at least never noticed, inner classes with more than one instance. – Valtteri Nov 16 '12 at 16:26
  • @Valtteri Exactly. See [Ted Hopp's answer](http://stackoverflow.com/questions/13420151/java-inner-class-usage-and-instantiation/13420335#13420335) for an explanation about doing the same thing with a named inner class. The two are equivalent as far as it concerns creating instances. – Code-Apprentice Nov 16 '12 at 16:28
3

In this code:

public void add(Buffer addi) {
    . . .
    uusi.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            img.setBuffer(added);
            main.getScr().updateUI();
        }
    });
}

The new ActionListener() {...} construct is an anonymous inner class. It behaves as if it were declared separately as a regular inner class. The main difference is that the name is generated automatically by the compiler. It is equivalent to:

private class Anonymous implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        img.setBuffer(added);
        main.getScr().updateUI();
    }
}

public void add(Buffer addi) {
    . . .
    uusi.addActionListener(new Anonymous());
}

Every time you execute your addActionListener code, it creates a new instance of this class.

Anonymous inner classes have a few other restrictions that are consequences of being anonymous. For instance, if they declare fields, the fields are only accessible (without using reflection) from within the class.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • The difference between the two being that the anonymous inner class has access to the `final` local variables of its containing context (i.e. `added`) but the `Anonymous` class as you have written it doesn't (because it's outside the method). You could move the `private class Anonymous` inside the `add` method to make it properly equivalent to the OP's code. – Ian Roberts Nov 16 '12 at 16:19
  • @mKorbel: Yeah, I just stuffed that line everywhere in hopes that it would solve some problems and it didn't seem to do any harm...will take notice for the future. – Valtteri Nov 16 '12 at 16:31
  • @IanRoberts - Excellent point about final local variables. I missed that one. – Ted Hopp Nov 16 '12 at 19:26
0

You are creating new anonymous classes for your menu item action listeners.

Basically, your code

menuItem.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
      img.setBuffer(added);
      main.getScr().updateUI();
  }
});

Defines a new class implementation for the ActionListener

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
0
  • main.getScr().updateUI(); isn't proper method for update already visible Swing Objects, this method is Look and Feel sensitive

  • use JPanel (don't forget to override PreferredSize otherwise returns Dimension (0, 0)) with paintComponent,

  • if you want ot remove then add a new container that hold Image then you have to call revalidate() and repaint()


  • use JList with images instead of JMenuItem

  • add ListSelectionListener to JList, then you can to switch betweens images


  • if isn't added any another JComponent(s) to the Container holds Image, then to use JLabel with Icon / ImageIcon
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • basic stuff, just to avoiding shots to the dark described here – mKorbel Nov 16 '12 at 16:19
  • @mKorbel Yes, I know someone would fire at me with these kind of advises :D Thanks for them anyway. I still don't really understand GUI and such basically at all, so this code is very spit and steel wire and it is a miracle it holds together... Will peruse your advises in any case. – Valtteri Nov 16 '12 at 16:24
  • [well because everything here is about ....](http://stackoverflow.com/questions/6988317/dynamically-add-components-to-a-jdialog/6989230#6989230) – mKorbel Nov 16 '12 at 16:25
0

What you're doing is creating an anonymous implementation of the interface ActionListener. Each anonymous implementation will have its own class file (called ImageList$1.class, ImageList$2.class and so on).

You could also do something like this, with similar results:

class MyActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      img.setBuffer(added);
      main.getScr().updateUI();
    }
};

menuItem.addActionListener(new MyActionListener());
Renato
  • 12,940
  • 3
  • 54
  • 85
0

An inner class inside a class is compiles into the 'parent' .class file. You can have multiple inner classes inside 1 class. (resulting in 1.class files after compilation) Every 'call' to an innerclass (runtime) will create an object of that innerclass (same as 'norma classes'). But it doen'st change your .class files.

So when add 3 buttons of the same type, then every time a new object of tha innerclass will be created. The same principle as for the button itself.

roel
  • 2,005
  • 3
  • 26
  • 41