Java can subclass inherit and override nested class?

2.8k Views Asked by At

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.

2

There are 2 best solutions below

1
jtahlborn On BEST ANSWER

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.

7
Boann On

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());
    }
}