4

This is for a Java Swing JToolBar:

I have my own Toolbar class that extends JToolBar, which has buttons on it with ActionListeners. Code:

class ToolBar extends JToolBar {
    JButton save, reset;

    ToolBar() {
        setFloatable(false);
        setRollover(true);
        makeButtons();
    }

    makeButtons() {
        save = new JButton();
        // Icon and tooltip code
        save.addActionListener(new ButtonListener());
        add(save);

        reset = new JButton();
        // Icon and tooltip code
        reset.addActionListener(new ButtonListener());
        add(reset);

    protected class ButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
        }
    }

Then I've made a few other subclasses of this one because I have need for multiple toolbars for multiple frames, each having a different "save" or "reset" source/target. I'll just give one for simplicity:

class FullToolBar extends ToolBar {

    protected class ButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            if (event.getSource() == save) FullInput.save();
            else if (event.getSource() == reset) FullInput.reset();
        }
    }
}

FullInput is the name of one of my JFrame's which the toolbar will go on. It has both a save and reset method which are static, but for some reason when I add the FullToolBar to my FullInput the buttons don't work.

Am I misunderstanding that nested classes can be inherited and overriden? Any ideas/comments or even suggestions of a completely different way to do what I'm trying to accomplish here? Basically, need a way to use the save and reset button on different frames/classes with the same toolbar.

Edward Guo
  • 175
  • 1
  • 13

2 Answers2

3

No, you can't override a nested class like that. there are a couple of ways you can tackle this. one would be to put the overrideable method on the main Toolbar class, e.g.:

class ToolBar extends JToolBar {

  makeButtons() {
    save = new JButton();
    // Icon and tooltip code
    save.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        saveButtonPressed(event);
      }
    });
  }

  protected void saveButtonPressed(ActionEvent event) {}
}

Then, a subclass can customize the action when the save button is pressed by overriding the saveButtonPressed method.

jtahlborn
  • 52,909
  • 5
  • 76
  • 118
  • I ended up using half your answer =P, I kept my nested class, but made method calls in the parent class (like you said) to empty methods, which were overridden in the subclasses. – Edward Guo Nov 04 '15 at 19:53
3

Am I misunderstanding that nested classes can be inherited and overriden?

Yes. Although your ButtonListener classes have the same name, they have no other connection to each other. Well... nested classes are inherited, but they cannot be overridden. For example, you could subclass an inherited class:

class FullToolBar extends ToolBar {
    protected class ButtonListener extends ToolBar.ButtonListener {
                                           ^^^^^^^^^^^^^^^^^^^^^^

That isn't useful here though, as it still won't override calls to new ButtonListener() in ToolBar.

There are many other ways to make this work. If you really want to override the ButtonListener class, you could replace uses of new ButtonListener() with calls to a method protected ActionListener createListener() to be overridden in subclasses, so they can provide whatever listener class implementation they want. In this case, that would probably be overcomplicating it though.

A simpler solution here is to make ToolBar implement ActionListener directly, and override the actionPerformed method in the subclass:

class ToolBar extends JToolBar implements ActionListener {
    ...

    void makeButtons() {
        ...
        save.addActionListener(this);
        ...
    }

    public void actionPerformed(ActionEvent event) {
    }
}

class FullToolBar extends ToolBar {
    @Override
    public void actionPerformed(ActionEvent event) {
        if (event.getSource() == save) FullInput.save();
        else if (event.getSource() == reset) FullInput.reset();
        else super.actionPerformed(event);
    }
}

As a variation on that, if you would like to prevent actionPerformed being public in ToolBar, have a protected method instead, named whatever you want, and override that in subclasses. Use Java 8's method reference syntax to connect the handler method with the buttons:

class ToolBar extends JToolBar {
    ...

    void makeButtons() {
        ...
        save.addActionListener(this::onButtonClick);
        ...
    }

    protected void onButtonClick(ActionEvent event) {
    }
}

class FullToolBar extends ToolBar {
    @Override
    protected void onButtonClick(ActionEvent event) {
        if (event.getSource() == save) FullInput.save();
        else if (event.getSource() == reset) FullInput.reset();
        else super.onButtonClick(event);
    }
}

Yet another idea: do you really need to have ToolBar set up the event listeners for those buttons at all? You could do that in the subclass:

class ToolBar extends JToolBar {
    ...

    protected void makeButtons() {
        save = new JButton();
        add(save);
        reset = new JButton();
        add(reset);
    }
}

class FullToolBar extends ToolBar {
    @Override
    protected void makeButtons() {
        super.makeButtons();
        save.addActionListener((ActionEvent e) -> FullInput.save());
        reset.addActionListener((ActionEvent e) -> FullInput.reset());
    }
}
Boann
  • 48,794
  • 16
  • 117
  • 146
  • Oh sorry, I didn't have static in there, that was just me testing to see if changing everything to static would do anything; edited it out. – Edward Guo Nov 04 '15 at 19:10
  • What does the @Override mean? – Edward Guo Nov 04 '15 at 19:14
  • @EdwardGuo [`@Override`](https://stackoverflow.com/questions/4341432/what-does-override-mean) is optional and doesn't change which method implementation is called, but it documents the fact that a method is being overridden, and lets the compiler warn about accidental typos of the overridden method name/signature. – Boann Nov 04 '15 at 19:20
  • Okay got it, thanks. One last small convention question. Is there a more commonly used way to use ActionListener between "implements ActionListener" and import java.awt.event.ActionListener;? or is it mostly just use case dependent. – Edward Guo Nov 04 '15 at 19:25
  • @EdwardGuo I'm not 100% sure what you mean but there are at least 4 ways to create an `ActionListener` instance. (1) Create a named class that `implements ActionListener`, as shown in your question. (2) Create an anonymous class `new ActionListener() { ... }` as shown in @jtahlborn's answer. (3) Use a method reference as shown in my answer (`save.addActionListener(this::onButtonClick);`). (4) Use lambda syntax: `save.addActionListener((ActionEvent e) -> FullInput.save());`. – Boann Nov 04 '15 at 19:44
  • Haha, yea that was a weird way to word my question. I meant ~of the different ways to create ActionListener, is one more commonly used than the others, or is it just different use case scenarios. – Edward Guo Nov 04 '15 at 19:49
  • @EdwardGuo Depends on personal preference. If you want a central event handler class for many components, then (1), a named class that implements `ActionListener`, and possibly implements other listener interfaces as well, makes sense. More commonly people separate the handlers for each component, which (2) and (3) and (4) make easy. (3) and (4) are shortest but have only become possible since Java 8, so they are less common. – Boann Nov 04 '15 at 20:06