![]() | |
|
Replacing Anonymous Inner Classes with Lambda Expressions
Portions of your code contain anonymous inner classes, which are sometimes difficult to follow. You would like to replace anonymous inner classes with code that is easier to read and maintain.
Replace the anonymous inner classes with lambda expressions. By doing so, development time will be much faster as there will be fewer lines of boilerplate code required. A typical Java Swing application utilizes anonymous inner classes to add functionality to application constructs. For instance, anonymous classes are a great way to add an action to a button. The problem is that inner classes can be difficult to follow, and they contain lots of boilerplate code.
The following lines of code demonstrate a typical anonymous inner class implementation for a button action implementation. Let's look at these lines of code before taking a look at how you can achieve the same solution using a lambda expression.
JButton button = ... JLabel comp = ... button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { comp.setText("Button has been clicked"); } });
In this example, we are creating a new object that provides an implementation of the
ActionListener
interface. This interface has a single method, actionPerformed
, which is
called by the button
instance when a user actually clicks the on-screen button. The
anonymous inner class provides the implementation of this method.
Anonymous inner classes were designed to make it easier for Java programmers to pass around code as data. Unfortunately, they do not make it easy enough. There are still four lines of boilerplate code required in order to call the single line of important logic.
Boilerplate is not the only issue, though: this code is fairly hard to read because it obscures the programmer's intent. We do not want to pass in an object; what we really want to do is pass in some behavior. In Java 8, we would write this code example as a lambda expression, as shown in example below:
JButton button = ... JLabel comp = ... button.addActionListener(e -> comp.setText("Button has been clicked"));
Instead of passing in an object that implements an interface, we are passing in a block of code - a function without a name.
The 'e
' is the name of a parameter (it can be any valid Java identifier), like the parameter
in the anonymous inner class example.
The '->
' separates the parameter from the body of the lambda expression, which is just
some code that is run when a user clicks the button.
Another difference between this example and the anonymous inner class is how we declare the variable
e
. Previously, we needed to explicitly provide its type - ActionEvent e
.
In this example, we have not provided the type at all, yet this example still compiles. What is
happening under the hood is that Java compiler is inferring the
type of the variable e
from its context - here, from the signature
of addActionListener
. It means that you do not need to explicitly write
out the type when it is obvious.
![]() | |
In some situations where the Java compiler cannot infer types, you MUST explicitly specify values for type variables with type witnesses. |
Example below demonstrates explicit type declaration (optional for this particular example) of lambda method parameter:
JButton button = ... JLabel comp = ... button.addActionListener((ActionEvent e) -> comp.setText("Button has been clicked"));
Syntax of Lambda Expressions
A lambda expression consists of the following:
A comma-separated list of formal parameters enclosed in parentheses.
Note: You can omit the data type of the parameters in a lambda expression. In addition, you can omit the parentheses if there is only one parameter. For example, the following lambda expressions are valid:
s -> s.getAge() >= 18
(Student s) -> s.getAge() >= 18
(s) -> s.getAge() >= 18
The arrow token ->
A body, which consists of a single expression or a statement block. This example uses the following expression:
... -> s.getAge() >= 18
If you specify a single expression, then the Java runtime evaluates the expression and
then returns its value. Alternatively, you can use a return
statement:
... -> { return s.getAge() >= 18; }
A return
statement is NOT an expression; in a lambda expression, you MUST enclose
statements in braces ({...}
). However, you do not have to enclose a void
method invocation in braces. For example, the following is a valid lambda expression:
email -> System.out.println(email)
java.util.function
package summary
There are a lot of re-usable functional requirements that can be captured by functional interfaces and lambdas.
The designers of Java 8.0 have captured the common use cases and created a library of functions for them. A
new package called java.util.function
was created to host these common functions.
![]() | |
Listings of the interfaces provided in this section are not complete. The listings only show the most important parts of the source code - signatures of the functional methods. |
java.util.function.Predicate
Parameter type: T
Return type: boolean
Description: Represents a predicate (boolean-valued function) of one argument.
package java.util.function; @FunctionalInterface public interface Predicate<T extends Object> { public boolean test(T t); }
java.util.function.Consumer
Parameter type: T
Return type: void
Description: Represents an operation that accepts a single input argument and returns no result.
package java.util.function; @FunctionalInterface public interface Consumer<T extends Object> { public void accept(T t); }
java.util.function.Function
Parameter type: T
Return type: R
Description: Represents a function that accepts one argument and produces a result.
package java.util.function; @FunctionalInterface public interface Function<T extends Object, R extends Object> { public R apply(T t); }
java.util.function.Supplier
Parameter type: None
Return type: T
Description: Represents a supplier of results. There is no requirement that a new or distinct result be returned each time the supplier is invoked.
package java.util.function; @FunctionalInterface public interface Supplier<T extends Object> { public T get(); }
java.util.function.ToDoubleFunction
Parameter type: T
Return type: double
Description: Represents a function that produces a double-valued result. This is
the double-producing primitive specialization for Function
.
package java.util.function; @FunctionalInterface public interface ToDoubleFunction<T extends Object> { public double applyAsDouble(T t); }
java.util.function.DoubleFunction
Parameter type: double
Return type: R
Description: Represents a function that accepts a double-valued argument and produces a
result. This is the double-consuming primitive specialization for Function
.
package java.util.function; @FunctionalInterface public interface DoubleFunction<R extends Object> { public R apply(double d); }
java.util.function.BiPredicate
Parameter types: T
, U
Return type: boolean
Description: Represents a predicate (boolean-valued function) of two arguments. This
is the two-arity specialization of Predicate
.
package java.util.function; @FunctionalInterface public interface BiPredicate<T extends Object, U extends Object> { public boolean test(T t, U u); }
java.util.function.UnaryOperator
Parameter types: T
Return type: T
Description: Represents an operation on a single operand that produces a result
of the same type as its operand. This is a specialization of
Function
for the case where the operand and result are of the same type.
NOTE: Since it extends Function
, it inherits Single Abstract Method -
Function.apply(Object);
, or more accurate T apply(T t);
package java.util.function; @FunctionalInterface public interface UnaryOperator<T extends Object> extends Function<T, T> { }
![]() ![]() ![]() |