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.
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
- class Dog(Animal) gives Dog the MRO Dog → Animal → object; Puppy(Dog) gives Puppy → Dog → Animal → object.
- 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.
- 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__.
- Calling the same method on a list of different objects runs each object's own version (polymorphism).
- 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).
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.
- 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?
Reveal 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.
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.
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.
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.
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.
- 1Choose the superclass
Extend AbstractAction, because the source states that actions are usually defined this way and that AbstractAction implements Action.
- 2Pass the descriptive name
Give the constructor the text Clear. This is the name property the action will carry.
- 3Define actionPerformed
Declare public void
actionPerformed(ActionEvent evt)inside the braces, since any concrete subclass must define it. Put the work inside:display.clear(). - 4Store the result as an Action
Assign the object to a variable of type Action named
clearAction, so it can later be passed to aJButtonconstructor.
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.
- The
JButtonconstructor receives the Action object, not the text Clear - The button's behavior is defined in
clearActionand itsactionPerformedbody
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.
| Feature | Anonymous AbstractAction subclass | Lambda expression |
|---|---|---|
| What it creates | An object of a subclass of AbstractAction, which is an Action | A function standing in for a single abstract method |
| Target requirement | Any concrete subclass must define actionPerformed | The target interface must be functional, with a single abstract method |
| Works for Action here | Yes | No, because Action is not a functional interface |
| Carries name and enabled status | Yes, through the AbstractAction constructor and its properties | Not 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 likeCreating a JButton with the text Clear and assuming the clearing logic lives in the button itself.
Why it failsThe source states that the button uses the behavior of an Action rather than containing it.
Do this insteadPut 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 likeWriting new AbstractAction("Clear") { } with an empty body and no actionPerformed method.
Why it failsAbstractAction declares
actionPerformedbut does not finish it, and any concrete subclass must define it.Do this insteadDefine 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 likeWriting Action a = (evt) -> display.clear(); in place of the anonymous class.
Why it failsA lambda can only stand in for a functional interface, and Action is not one.
Do this insteadKeep 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
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
JButtonconstructor 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
JButtoncan 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.