Swing and Java 8
Posted by Darryl Burke on April 5, 2015
Java 7 brought us JLayer (which I haven’t played with), Generics in JComboBox and JList, and a lonely collections-oriented JList#getSelectedValuesList(). Java 8, on the other hand, did not introduce any new Swing API. However, enhanced language features can certainly make Swing code less verbose and more maintainable, and the new Date/Time API makes it easier than ever before to create interactive Swing components for date and/or time selection.
Chief among the new Java language features is the much-awaited lambda (or ƛ) expression, which can replace an anonymous inner class with just one line of code. This is what traditional code to launch a Swing GUI looks like:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SwingGui().createUI();
}
});
Using a lambda expression reduces those 6 lines to:
SwingUtilities.invokeLater(() -> new SwingGui().createUI());
The same applies to listener code. The design choice to implement listeners in the main GUI class, adopted by many, did allow for shorter code than anonymous inner classes, but also made the main classes prone to misuse. With lambdas and their companion, method references, we have the best of both worlds: one line of code to add a listener that invokes a method in the GUI class, without the slightest need for that class to implement the listener interface.
myButton.addActionListener(ae -> buttonClickAction());
...
...
public void buttonClickAction() {
// react to button click
}
By providing a method that can accept an ActionEvent parameter, you can use a method reference instead.
myButton.addActionListener(this::buttonClickAction);
...
public void buttonClickAction(ActionEvent ae) {
// react to button click
}
Further, a lot of listener code ignores the passed-in event object; a single method will then suffice for different types of listeners. All that is needed is that the listener be a functional interface, that is, it must have only one method to be implemented, and that the method referenced must be able to accept the parameter passed to the listener’s functional method.
myButton.addActionListener(this::listenerMethod);
mySpinner.addChangeListener(this::listenerMethod);
myList.addListSelectionListener(this::listenerMethod);
myTree.addTreeSelectionListener(this::listenerMethod);
...
public void listenerMethod(EventObject /* or Object */ ignored) {
// get values from components and process them
}
Going back to launching a Swing GUI, if the createUI() method is static, you can use a method reference there too:
SwingUtilities.invokeLater(SwingGui::createUI);
And if the GUI is created and set visible in a no-arg constructor, this will do it:
SwingUtilities.invokeLater(SwingGui::new);
If you are using Java 8, we hope that this brief introduction will help you reduce several lines of code and the attendant indentation associated with anonymous inner classes, making your program less cluttered and more maintainable. Future blog posts will focus on the use of the new Date/Time API with JSpinner, leading up to a composite component for selecting a LocalDate.
Related Reading
The Java™ Tutorials:
Lambda Expressions
Method References
When to Use Nested Classes, Local Classes, Anonymous Classes, and Lambda Expressions
Jack said
Thanks a lot.
Very nice.
Jack said
This will also work:
final JButton jButton = new JButton(“Click me”);
jButton.addActionListener(event -> {
});
Darryl Burke said
Thank you, Jack.
With Java 8, you no longer need to declare local variables as final. A variable may be ‘deemed’ final if it a value is assigned only once.
Using a lambda expression with a block statement is covered in the linked Oracle tutorials.
Darryl
Jack said
Thanks again.
This is Eclipse preference…..