Concepts / Attaching ActionListeners to Components

Attaching ActionListeners to Components

The Action interface represents an action together with properties such as a name, an icon, and an enabled or disabled status.

  • Programming
Interactive lab

Try it: Inheritance and Method Lookup

How a subclass inherits from its parent, how Python finds an attribute by walking the instance, then the class's method resolution order up to object, how overriding and super() work, why the same call does different things on different objects (polymorphism), and what isinstance / issubclass check.

How it works

  1. class Dog(Animal) gives Dog the MRO Dog → Animal → object; Puppy(Dog) gives Puppy → Dog → Animal → object.
  2. obj.name searches obj.__dict__, then each class of type(obj).__mro__ in order; the first class that defines the name wins, so a subclass method overrides the parent's.
  3. super().method() starts the search in the class after the one the current method was defined in, e.g. to run the parent's __init__.
  4. Calling the same method on a list of different objects runs each object's own version (polymorphism).
  5. isinstance(obj, C) is True when C is in type(obj).__mro__; if no class has the name, Python raises AttributeError.

Default run (63 steps): Ready: Animal, Dog, Cat and Puppy, then the statements you picked. Each attribute is searched along the class's MRO. … Finished. Printed: Rex the dog says Woof / Tom the cat says Meow / Bo the dog says Woof! / True / Puppy / Dog / Animal / object.

Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Single inheritance only (the lab's MRO code is Python's C3 algorithm, but no class here has two parents).

Educational simulation

Loading the simulation…

Two Routes to a Button's Behavior

Up to this point, a push button in a Java GUI has probably been built in a familiar way. You create the button with a text label, then attach an ActionListener that runs when the user presses it. That approach works well for a single button. This article introduces a second route: packaging the behavior into an Action object first, and then handing that object to a button.

The difference matters because the Action object is more than a piece of code. It describes the action as something a user can see and control, and it also defines what happens when the action is performed. Once you can tell those two roles apart, the rest of the approach becomes easy to follow.

Reading the Clear Declaration

Before any explanation, read the following declaration. A single statement both names the action and defines what happens when it is performed. Your job is to work out which piece of the code is responsible for the button's behavior and which piece only describes the action.

java
Notes on the code
  • The declared variable has type Action and is named clearAction
  • The text Clear is passed to the AbstractAction constructor as a property of the action
  • The braces define an anonymous subclass that holds the behavior

What do you think happens?

If a button is later built from clearAction and the user presses it, which part of this declaration does the work?

  • The string Clear passed to the constructor

  • The variable name clearAction

  • The actionPerformed body inside the braces

Reveal answer
Answer:

The actionPerformed body inside the braces, which calls display.clear().

The name and the Clear text describe the action for the user, but the method body is the code that runs when the action is performed. The rest of this article unpacks why.

What an Action Carries

The Action interface represents an action together with descriptive properties. The properties named in the source are a name for the action, an icon that represents the action, and whether the action is currently enabled or disabled. These properties describe the action itself, separate from the code that runs when the action fires.

This bundling is why the idea is useful. The same object can carry a label, an icon, and an enabled state, so any component that uses the action can present itself consistently. Notice that these properties belong to the action, not to one particular button. A button is only one of the things that can use an action, and that distinction becomes important later.

NameClearIconan icon representing theactionEnabled statusenabled or disabled
What set of named properties does an Action object hold, and how does each property map to a value?
Key point

The properties describe the action as something a user can see and control. The behavior that runs when the action fires is stored separately, inside actionPerformed.

How Action, AbstractAction, and the Subclass Fit Together

Actions are usually defined using the class AbstractAction. This class is abstract, and it implements the Action interface. Because it implements Action, every AbstractAction object is also an Action, which is the type a button needs in order to accept it.

Definition

AbstractAction declares the method public void actionPerformed(ActionEvent evt). The class declares this method, but a subclass must define it. Any concrete subclass has to provide the body of actionPerformed, so AbstractAction supplies the structure and the programmer supplies the behavior that runs when the action is performed.

Action interfacedefines what an action isAbstractActionimplements Action, declaresactionPerformedAnonymous subclassdefines actionPerformedbodyJButtonuses the action, does notcontain the behaviorclearActionAction objectimplementsextendsis an instance ofuses
How are the JButton and the Action object related, and which one owns the behavior that runs on click?

Read the diagram from the top down. The interface defines what an action is. The abstract class implements that interface and declares actionPerformed without finishing it. A concrete subclass finishes the job. The button sits at the bottom: it does not implement the behavior itself, it uses an Action that somebody else built.

Tracing the Anonymous Subclass Line by Line

The declaration from earlier does three things at once. It declares a variable of type Action named clearAction. It calls the AbstractAction constructor with the text Clear. And it opens a braced body that defines a subclass, which is what makes this an anonymous inner class. The source describes this pattern as the usual way to define actionPerformed, because it keeps the behavior next to the place where the action is created.

The text Clear is a property of the action, in the sense described earlier. The braces hold the behavior. Inside them, actionPerformed calls display.clear(). The source assumes display is an object that has a clear() method. The behavior is therefore an ordinary method call written inside the anonymous subclass.

Worked example

Creating an Action that clears a display

Write an Action that clears a display when it is performed, then prepare it for use by a button.

  1. 1
    Choose the superclass

    Extend AbstractAction, because the source states that actions are usually defined this way and that AbstractAction implements Action.

  2. 2
    Pass the descriptive name

    Give the constructor the text Clear. This is the name property the action will carry.

  3. 3
    Define actionPerformed

    Declare public void actionPerformed(ActionEvent evt) inside the braces, since any concrete subclass must define it. Put the work inside: display.clear().

  4. 4
    Store the result as an Action

    Assign the object to a variable of type Action named clearAction, so it can later be passed to a JButton constructor.

Result

clearAction is an Action whose name is Clear and whose actionPerformed body clears the display. No separate listener has been attached to any button yet.

Building a JButton from an Action

The source notes that a JButton can be created with a constructor that takes either text or an Action object. Once you have an Action, you can pass it to the JButton constructor directly. The button then takes its behavior from that action rather than from a separate listener you attach afterward. Compare this with the familiar approach: there the behavior is attached to the button after creation, while here the behavior already lives in the Action when the button is built.

java
Notes on the code
  • The JButton constructor receives the Action object, not the text Clear
  • The button's behavior is defined in clearAction and its actionPerformed body
UserclearButtonJButtonclearActionAction objectactionPerformeddisplay.clear()pressesuses its actionruns body
When the user clicks the button, what object receives the event first, and what method gets called next?

Read the sequence from left to right. The user presses the button, the button relies on the Action it was built from, and the actionPerformed body of that action is what runs. The button never contains the clearing logic. Changing the behavior means changing the action, and any other component built from the same action would share that change.

Why a Lambda Cannot Replace the Subclass

Java programmers often reach for lambda expressions to shorten code, so it is natural to wonder whether the anonymous class could become a lambda. The source addresses this directly: a lambda expression cannot be used here, because Action is not a functional interface. A lambda can only stand in for an interface with a single abstract method, and Action does not qualify.

FeatureAnonymous AbstractAction subclassLambda expression
What it createsAn object of a subclass of AbstractAction, which is an ActionA function standing in for a single abstract method
Target requirementAny concrete subclass must define actionPerformedThe target interface must be functional, with a single abstract method
Works for Action hereYesNo, because Action is not a functional interface
Carries name and enabled statusYes, through the AbstractAction constructor and its propertiesNot applicable as an Action object

Anonymous subclass versus lambda for creating an Action

Mistakes When Writing Action Objects

  • Common mistake

    Expecting the button to hold the click behavior

    What it looks like

    Creating a JButton with the text Clear and assuming the clearing logic lives in the button itself.

    Why it fails

    The source states that the button uses the behavior of an Action rather than containing it.

    Do this instead

    Put the behavior in the actionPerformed body of the Action, then pass that Action to the JButton constructor.

  • Common mistake

    Leaving actionPerformed out of the subclass

    What it looks like

    Writing new AbstractAction("Clear") { } with an empty body and no actionPerformed method.

    Why it fails

    AbstractAction declares actionPerformed but does not finish it, and any concrete subclass must define it.

    Do this instead

    Define public void actionPerformed(ActionEvent evt) inside the braces with the required behavior.

  • Common mistake

    Trying to replace the anonymous subclass with a lambda

    What it looks like

    Writing Action a = (evt) -> display.clear(); in place of the anonymous class.

    Why it fails

    A lambda can only stand in for a functional interface, and Action is not one.

    Do this instead

    Keep the anonymous subclass of AbstractAction for the Action object.

  • Common mistake

    Confusing the Clear text with the behavior, when the text only names the action

Tracing the Action Flow Yourself

MEDIUM

A colleague writes a new Action named saveAction whose actionPerformed body calls file.save(). They build a JButton from saveAction and then ask why changing the button's text does not change what happens when it is pressed. Explain which object owns the behavior that runs on click, and identify the exact method that holds it.

Hints
  • Look back at which object the JButton constructor receives
  • Recall which method the source says every concrete subclass must define
  • Think about whether the name property controls the behavior or only describes the action

Key Takeaways

  • The Action interface represents an action together with descriptive properties: a name, an icon, and an enabled or disabled status.
  • AbstractAction implements Action and declares actionPerformed, which every concrete subclass must define. The programmer supplies the behavior inside that method.
  • An anonymous inner subclass of AbstractAction is the usual way to define an Action, keeping the behavior next to the place where the action is created.
  • A JButton can be constructed from an Action object, so the button uses the behavior of that action rather than containing it.
  • A lambda expression cannot replace the anonymous subclass because Action is not a functional interface.