Concepts / Lambda Expressions in Java

Lambda Expressions in Java

An ActionListener is a functional interface whose single method, actionPerformed, receives an ActionEvent, so a lambda expression can supply it.

  • Programming

A Panel That Never Refreshes

Imagine a panel on the screen that is supposed to redraw itself after a short pause. The code that sets this up looks almost complete, yet the panel never changes. Nothing crashes, and nothing prints an error. The question this article answers is how a timer, a listener, and a single line of lambda code work together to make the panel repaint, and how to find the exact point where that chain breaks.

A timer on its own only counts time. For it to be useful, something must happen when the counted time has passed. In Java Swing, that something is supplied by an object that responds to the timer's events. The program in this article is a panel class named RandomArt, whose constructor creates a timer that repaints the panel after a delay. We will start from that working program and then work backward through each piece that makes it run.

What do you think happens?

The constructor creates a Timer with a listener, but the line timer.start() is accidentally deleted. What happens when the delay would have elapsed?

  • The listener runs once the delay passes

  • The listener never runs, even though its code is correct

  • The program throws an exception at the moment the delay would end

Reveal answer
Answer:

The listener never runs, even though its code is correct

The source is explicit that a Timer that was constructed but never started will never reach its listener. Creating the timer and configuring it do not begin the countdown; start() is the call that does.

Following the Hand-Offs from start() to repaint

It helps to think of the program as a sequence of hand-offs. The constructor creates and configures the timer, then starts it. Later, when the delay has elapsed, the timer notifies its listener. The listener runs its body, which calls repaint() on the panel. Each arrow in the diagram below is one of these hand-offs, labelled with the call that carries control from one part to the next.

ConstructorTimerLambda listenerRandomArt panelnew Timer(4000, listener)start()delay elapses: actionPerformed(evt)repaint()
After timer.start() is called, which objects and methods are involved, in what order, before actionPerformed runs?

Read the diagram as a chain. If any link is missing, the later steps never happen. The most important link is the first one. A Timer that was constructed but never started will never reach its listener, and the listener's code can be perfectly correct and still never run. When you debug a timer-driven panel, the chain tells you where to look: a missing start() call, a listener body that does the wrong thing, or a repaint call that does not reach the panel you are watching.

Reading the Timer Constructor Call

The constructor belongs to the RandomArt panel class. It does three things, which you can remember as create, configure, start. It creates a timer with a delay of 4000 milliseconds, supplies a lambda expression as the listener, and then starts the timer. The source omits the rest of the class, so the code below is a reconstruction of the constructor as described, kept deliberately small so the three steps stand out.

java
Notes on the code
  • new Timer(...) creates the timer object
  • 4000 is the delay in milliseconds
  • e -> repaint() is the ActionListener, written as a lambda
  • timer.start() begins the countdown

The Timer constructor takes two arguments. The first is the delay, expressed in milliseconds, so 4000 means four seconds. The second is the ActionListener that will be notified when that delay has elapsed. Because ActionListener is a functional interface, the second argument can be written as a lambda expression rather than as a reference to a separately declared object. In this constructor, the name e is simply the name chosen for the listener's parameter, and the body repaint() does not have to use it.

What the Listener Receives

Definition

An ActionListener is a functional interface whose single method, actionPerformed, receives an ActionEvent. Because the interface has only one method, a lambda expression can supply the whole implementation.

A lambda expression is a compact way of writing an object that implements a functional interface. The compiler can match the lambda to the interface's one method without any extra naming, so the lambda only needs the parameter and the body. The method signature is the contract: any code handed to the timer as a listener must fit the signature actionPerformed declares, which takes an ActionEvent parameter. The lambda's parameter, such as e, is the name you give that incoming event within the body.

Lambda and Traditional Forms Side by Side

The traditional form of the same listener declares a class that implements ActionListener and writes out its actionPerformed method in full. The lambda form attaches that behaviour directly where the Timer is created. The diagram below lines up the parts of the two forms so you can see which piece corresponds to which.

Parameter eParameter evtBody repaint()Body repaint()Attached at TimercreationDeclaredactionPerformedmethodsame signaturesame behavioursame contract
How do the lambda form and the anonymous ActionListener class line up part by part, and why do they behave the same?
java
Notes on the code
  • new ActionListener() { ... } declares the listener as an anonymous class
  • public void actionPerformed(ActionEvent evt) is the method the timer calls
  • evt is the traditional name for the ActionEvent parameter
  • repaint() is the same body as in the lambda version
Worked example

Rewriting a lambda listener in traditional form

Rewrite the lambda listener e -> repaint() as a traditional ActionListener, and explain why the timer behaves the same either way.

  1. 1
    Identify the functional interface

    The lambda is supplied to a Timer, which expects an ActionListener. ActionListener has one method, actionPerformed.

  2. 2
    Write the implementing class

    Declare new ActionListener() { ... } and place a public void actionPerformed(ActionEvent evt) method inside it.

  3. 3
    Move the body across

    The lambda body repaint() becomes the body of actionPerformed. The parameter e becomes evt, which is only a name.

  4. 4
    Check the signature

    The method must accept an ActionEvent, matching the contract the Timer relies on when it notifies its listener.

  5. 5
    Compare the behaviour

    Both versions are passed to the same Timer constructor and both repaint the panel when the delay elapses, so the timer does the same thing either way.

Result

The traditional form has the same signature, body, and effect as the lambda. Only the amount of text differs; the timer still calls actionPerformed after the delay and the panel still repaints.

Finding Where a Broken Timer Stops

When a timer-driven panel does not update, work through the chain in order and stop at the first link that is missing or wrong. The flowchart below turns that checklist into a sequence of questions. Notice that the first question concerns start(), because a timer that was never started never reaches its listener, however good the listener's code is.

Timer constructedstart called?Listener never runsDelay elapsesListener bodycorrect?Wrong body runswithout errorrepaint reaches thepanel?Panel updatesPanel stays stalenoyesnoyesnoyes
At which step in the timer-to-listener chain does a broken timer halt, and what checks distinguish a working timer from a broken one?
MEDIUM

A colleague's RandomArt panel never repaints. The constructor creates new Timer(4000, e -> repaint()) and the line timer.start() is present. The listener body was recently edited to read e -> System.out.println(e) and nothing else. Using the chain from the flowchart, identify which link is broken, explain why the program raises no error, and describe the change that would restore repainting.

Hints
  • Start at the first check in the flowchart: was start() called?
  • Then check the listener body. A lambda with a wrong body runs without complaint.
  • Remember that repaint() is the call that reaches the panel.

Mistakes Beginners Make with Timer Listeners

  • Common mistake

    Creating the timer without calling start()

    What it looks like

    Timer timer = new Timer(4000, e -> repaint()); with no timer.start() after it

    Why it fails

    A Timer that was constructed but never started will never reach its listener, so the delay never elapses from the listener's point of view.

    Do this instead

    Call timer.start() after the constructor has configured the timer.

  • Common mistake

    Assuming a correct-looking lambda must be the one that runs

    What it looks like
    Timer timer = new Timer(4000, e -> System.out.println(e));
    Why it fails

    A lambda with a wrong body runs without complaint, so the program looks healthy while the panel never repaints.

    Do this instead

    Check that the body calls repaint() on the panel you are watching.

  • Common mistake

    Thinking the lambda parameter must be named evt

    What it looks like

    Writing e -> repaint() and wondering why the name does not match the traditional declaration

    Why it fails

    The name e is only the name chosen for the parameter; the body does not have to use it, and the compiler matches the lambda to the method by its single method rather than by name.

    Do this instead

    Choose any parameter name that reads clearly, and use the traditional evt name only when writing the anonymous class form.

  • Common mistake

    Treating the delay as seconds instead of milliseconds

Checking Your Understanding of the Hand-Offs

EASY

Explain, in your own words, why creating a Timer and passing it a listener is not enough for the listener to run, and name the three things the RandomArt constructor does in order.

Hints
  • Think about the difference between configuring a timer and starting its countdown.
  • Create, configure, start is the order the constructor follows.

Key Takeaways

  • A Timer takes a delay in milliseconds and an ActionListener; the listener is notified when the delay has elapsed, but only after timer.start() has been called.
  • ActionListener is a functional interface whose single method, actionPerformed, receives an ActionEvent, which is why a lambda expression can supply it.
  • A lambda such as e -> repaint() and its traditional anonymous class form have the same signature and body, so the timer behaves the same either way.
  • To debug a timer-driven panel, trace the chain from constructor to start() to listener to repaint, and find the first link that is missing or wrong.