Lambda Expressions in Java
An ActionListener is a functional interface whose single method, actionPerformed, receives an ActionEvent, so a lambda expression can supply it.
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?
Reveal 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.
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.
- new
Timer(...)creates the timer object - 4000 is the delay in milliseconds
- e ->
repaint()is theActionListener, 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
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.
- 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
ActionEventparameter repaint()is the same body as in the lambda version
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.
- 1Identify the functional interface
The lambda is supplied to a Timer, which expects an
ActionListener.ActionListenerhas one method,actionPerformed. - 2Write the implementing class
Declare new
ActionListener(){ ... } and place a public voidactionPerformed(ActionEvent evt)method inside it. - 3Move the body across
The lambda body
repaint()becomes the body ofactionPerformed. The parameter e becomes evt, which is only a name. - 4Check the signature
The method must accept an
ActionEvent, matching the contract the Timer relies on when it notifies its listener. - 5Compare 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.
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.
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 likeTimer timer = new Timer(4000, e -> repaint()); with no timer.start() after it
Why it failsA 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 insteadCall 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 likeTimer timer = new Timer(4000, e -> System.out.println(e));Why it failsA lambda with a wrong body runs without complaint, so the program looks healthy while the panel never repaints.
Do this insteadCheck that the body calls repaint() on the panel you are watching.
Common mistake Thinking the lambda parameter must be named evt
What it looks likeWriting e -> repaint() and wondering why the name does not match the traditional declaration
Why it failsThe 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 insteadChoose 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
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 aftertimer.start()has been called. ActionListeneris a functional interface whose single method,actionPerformed, receives anActionEvent, 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.