Concepts / Anonymous Event Handlers and Adapter Classes

Anonymous Event Handlers and Adapter Classes

An anonymous inner class is a class defined without a name, and a single new expression both declares it and creates an object of it

  • Programming

A Click Handler Written in One Statement

Most graphical Java programs respond to the mouse. A button is pressed, a panel is clicked, or a pointer moves across a canvas, and the program has to run some code in reply. Java expresses that reply through listener objects. This article covers one of the most common ways to write them: directly at the point where they are needed, with no separate named class.

Read the program below before reading any explanation. Ask yourself two questions: which method is actually written by the programmer, and which mouse events does the program leave unhandled? Keep your answers in mind, because the rest of the article checks them.

java

The statement above is a complete listener. It is created and registered in one step, and it has no variable name. The rest of this article explains each part of it and shows what happens when the code is changed.

What One Expression Does Two Jobs

An anonymous inner class is a class defined inside a method or inside another class, and it has no name. Java lets you build such an object with a special form of the new operator. You write new, then the name of the interface or class being implemented, then a block of method definitions in braces. One expression therefore does two jobs at once: it declares an unnamed class and produces an object that belongs to that class.

panel.addMouseListener(registers the listener withthe componentnew MouseAdapter()creates the object with thenew operator{ ... }class body: the methoddefinitions of the unnamedclass)closes the method callargumentbody followsthen
Which parts of this one expression declare the unnamed class, which part creates its object, and where does the class body begin and end?

Read the statement from left to right. The first token, panel.addMouseListener(, is an ordinary method call on the panel. The second token, new MouseAdapter(), is the object creation. The braces that follow contain the class body, which is the declaration part of the same expression. The closing parenthesis ends the method call, because the object was written directly as its argument.

Because the class has no name, you cannot refer to it later by name. What you keep is the object that the expression produces, and that object is what gets handed to something else. An object that will receive mouse events has to be registered with the component that produces them, using the addMouseListener() method of that component. The anonymous object is written directly as the argument to that method, so the listener is created and registered in a single statement. No variable is needed, because the component keeps its own reference to the listener once it has been added.

Why Adapter Classes Exist

In a typical application you care about only one or two of the five mouse methods. The rest must still exist, because the listener interface demands them. A class that implements an interface must define every method of that interface, even as empty definitions. The result is a listener in which most of the bodies are empty. The code is correct, but it is tedious to write and tiring to read, and nearly all of its lines convey nothing about what the program does.

The problem is one of obligation rather than of logic. An empty method is not a mistake, but the interface forces you to write it anyway. An adapter class removes that obligation. An adapter such as MouseAdapter implements a listener interface by giving an empty definition for every method in that interface. The adapter is complete from the compiler's point of view, but it does nothing useful on its own. Its value lies in being extended: you create a subclass of the adapter and define only the methods whose behavior you want. Every method you leave out keeps the empty definition inherited from the adapter.

MouseListenerinterface with five mousemethodsMouseAdapterempty definitions for everymethodAnonymous subclassoverrides only mousePressedMouseMotionListenerinterface for movementmethodsimplementsimplementsextends
Which methods does the anonymous class inherit as empty stubs from MouseAdapter, and which one does it override to supply its own behavior?

The diagram shows the chain of responsibility. MouseAdapter already satisfies both interfaces, so any listener that extends it inherits every required method. MouseAdapter implements both the MouseListener interface and the MouseMotionListener interface, which makes it a convenient base for a listener that should respond to any mouse event, whether a click or a movement. The subclass is where your own choices live: you decide which events the listener actually handles.

AspectImplementing the listener interface directlyExtending MouseAdapter
Methods you must writeAll five MouseListener methodsOnly the methods you use, such as mousePressed
Empty definitions in your codeOne for each method you do not needNone; they are inherited from the adapter
Lines a click-only listener needsMany, most of them emptyA few, containing your real behavior

The practical difference between the two styles of anonymous listener

Following a Click Through the Listener

What do you think happens?

In the panel example above, the user presses the mouse button on the panel and then releases it. How many lines are printed?

  • Zero

  • One

  • Two

Reveal answer
Answer:

One

The listener overrides only mousePressed, so the press runs the programmer's statement and prints one line. Releasing the button calls mouseReleased, which the listener inherits from MouseAdapter as an empty definition, so it prints nothing.

Once the anonymous object is registered, the component holds a reference to it. When a mouse event of a kind the listener handles occurs on that component, the method with the matching name is the one the program runs. For the listener above, mousePressed is the only method with real code, so pressing the mouse button is the only event that leads to your statement. Other mouse events still reach the listener, but they run inherited empty definitions and so do nothing visible.

Userpresses and releases thebuttonPanelthe component that producesthe eventAnonymous listenerholds the registeredlistener1. press2. mousePressed(e) runs your body3. release4. mouseReleased(e) runs inherited empty body
When the user clicks, what object receives the event first, and which method on the anonymous object is called in what order?

Tracking this path is the core debugging skill for listener code. At each step, ask which object is holding the listener, which method name matches the event, and whether that method has a body you wrote or an empty body inherited from the adapter. When behavior is missing, the failure usually sits at one of those three points.

Mistakes That Silently Break a Listener

Both of the following mistakes compile and run without error, which is why they are easy to miss. In each case the listener is registered, so the program looks correct, but the handler that should respond never does anything useful.

  • Common mistake

    Leaving the handler body empty

    What it looks like
    panel.addMouseListener(new MouseAdapter() {
        @Override
        public void mousePressed(MouseEvent e) {
        }
    });
    Why it fails

    The method is written, so the adapter's empty version is replaced by yours, but your version has no statements. Pressing the button runs an empty body, and nothing is printed or changed.

    Do this instead

    Put the intended statements inside the body of mousePressed.

  • Common mistake

    Misnaming the handler method

    What it looks like
    panel.addMouseListener(new MouseAdapter() {
        public void mousePress(MouseEvent e) {
            System.out.println("Button pressed");
        }
    });
    Why it fails

    mousePress does not match any method of the listener interfaces, so it does not override anything. The inherited mousePressed is still an empty definition, so the press does nothing. Because mousePress is not an override, the @Override annotation would catch this as a compile error if it were added.

    Do this instead

    Use the exact method name mousePressed, and add @Override so the compiler checks the name.

  • Common mistake

    Forgetting addMouseListener(), so the object is created but never registered, meaning no mouse event ever reaches it

Practice Locating the Fault

MEDIUM

A programmer wants a panel to print "Released" when the mouse button is released on it. The code below compiles, but nothing is printed. Identify the exact line where the problem is, and write the corrected version of the listener.

panel.addMouseListener(new MouseAdapter() {
    @Override
    public void mousePressed(MouseEvent e) {
        System.out.println("Released");
    }
});
Hints
  • Ask which method runs when the button is released, and which method the programmer wrote.
  • The method that runs on release is mouseReleased, inherited from MouseAdapter as an empty definition.
  • The corrected listener overrides mouseReleased instead of mousePressed.

Key Takeaways

  • A single new expression both declares an unnamed class and creates an object of that class, and the object is passed directly as the argument to addMouseListener().
  • An adapter class such as MouseAdapter supplies empty definitions for every method, so a subclass only needs to override the methods it actually uses.
  • When a mouse event occurs, the method with the matching name runs; if you wrote an empty body or misnamed the method, the inherited empty definition runs instead and nothing visible happens.
  • To debug a listener, check which object holds it, which method name matches the event, and whether that method has your body or an inherited empty one.