Lambda ExpressionsJ8 Home « Lambda Expressions
In the previous lesson we took an overview of Functional Interfaces which were introduced in Java8 and are interfaces containing a single abstract method; this does not include declaration of an abstract method overriding one of the public methods of java.lang.Object since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.
But what do Functional Interfaces allow us to do? They allow us to use lambda expressions or method references to create a block of code that is available to process without executing it. This deferred execution allows us to pass the code as an argument to a method that will be executed later. This is a software development pattern known as behaviour parameterization and allows us to parameterize the method based on the passed block of code. We can derive a function descriptor from a Functional Interface that can be used via assignment of a lambda expression or method reference.
Function Descriptors Top
Lets take another look at the Predefined Functional Interfaces table with the derived function descriptor for each.
| Functional Interface | Function Descriptor |
|---|---|
Within java.util.function package | |
BiConsumer<T,U> | (T, U) -> void |
BiFunction<T,U,R> | (T, U) -> R |
BinaryOperator<T> | (T, T) -> T |
BiPredicate<T,U> | (T, U) -> boolean |
Consumer<T> | T -> void |
Function<T,R> | T -> R |
Predicate<T> | T -> boolean |
Supplier<T> | () -> T |
UnaryOperator<T> | T -> T |
| Existing interfaces that are functional in Java8 | |
ActionListener | (T, U) -> void |
Callable | () -> V |
Comparator | (E) -> void |
Runnable | () -> void |
Lambda Expression Syntax Top
A lambda expression consists of 3 parts (argument-list) -> {body}
- Argument-list: Can be empty or non-empty.
- Arrow-token: Used to link argument-list and body of expression.
- Body: Expressions and statements for lambda expression.
Following are the various flavours of a lambda expression with 0, 1 and 2 parameters:
// NO PARAMETER LAMBDA
() -> expression
() -> {}
// ONE PARAMETER LAMBDA
// parameter -> expression
p -> expression
// parameter -> code block
p -> {}
// TWO PARAMETER LAMBDA
// parameter1, parameter2 -> expression
(p1,p2) -> expression
// parameter1, parameter2 -> code block
(p1,p2) -> {}
Lets take a look at some code examples of lambda expressions starting with consumers.
Consumer and BiConsumer Examples Top
The Consumer functional interface represents an operation upon a single argument that produces no result whose functional method is accept(Object). Contrary to most other functional interfaces, Consumer is expected to operate via side-effects.
The BiConsumer functional interface represents an operation upon two arguments that produces no result whose functional method is accept(Object, Object). This is a two-arity specialization of the Consumer functional interface. Contrary to most other functional interfaces, BiConsumer is expected to operate via side-effects.
Following is a code sample using the Consumer and BiConsumer functional interfaces.
package info.java8;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class ConsumerLambda {
public static void main(String[] args) {
List<String> numbersAsStringOne = Arrays.asList("one", "two", "three", "four", "five");
List<String> numbersAsStringTwo = Arrays.asList("one", "two", "three", "four", "six");
List<String> numbersAsStringThree = Arrays.asList("one", "two", "three", "four");
List<String> numbersAsStringFour = Arrays.asList("one", "two", "three", "four", "five");
// Consumer
Consumer<String> consumerMethod = s -> System.out.println(s);
numbersAsStringOne.forEach( consumerMethod );
// BiConsumer
BiConsumer<List<String>, List<String>> biConsumerMethod = (list1, list2) -> {
if (list1.size() != list2.size()) {
System.out.println("List sizes are not equal");
}
else {
for (int i = 0; i < list1.size(); i++)
if (!Objects.equals(list1.get(i), list2.get(i))) {
System.out.println("List contents are not equal");
return;
}
System.out.println("List contents are equal");
}
};
biConsumerMethod.accept(numbersAsStringOne, numbersAsStringTwo);
biConsumerMethod.accept(numbersAsStringOne, numbersAsStringThree);
biConsumerMethod.accept(numbersAsStringOne, numbersAsStringFour);
}
}
Save, compile and run the ConsumerLambda class in directory c:\_OOConcepts in the usual way.
ConsumerLambda class.The above screenshot shows the output of running our ConsumerLambda class.
Function and BiFunction Examples Top
The Function functional interface represents an operation that accepts a single argument and produces a result whose functional method is apply(Object).
The BiFunction functional interface represents an operation that accepts two arguments and produces a result whose functional method is apply(Object, Object). This is a two-arity specialization of the Function functional interface.
Following is a code sample using the Function and BiFunction functional interfaces.
package info.java8;
import java.util.function.BiFunction;
import java.util.function.Function;
public class FunctionLambda {
public static void main(String[] args) {
// Function
Function<Integer, Integer> functionMethod = a -> (a * a);
System.out.println("Square: " + functionMethod.apply(5));
// BiFunction
BiFunction<Integer, Integer, Integer> biFunctionMethod = (a, b) -> (a * b);
System.out.println("Product: " + biFunctionMethod.apply(7, 10));
}
}
Save, compile and run the FunctionLambda class in directory c:\_OOConcepts in the usual way.
FunctionLambda class.The above screenshot shows the output of running our FunctionLambda class.
UnaryOperator and BinaryOperator Examples Top
The UnaryOperator functional interface represents an operation upon a single operand, producing a same type result as the operand whose functional method is Function.apply(Object). This is a specialization of the Function functional interface for situations where the operand and result are of the same type.
The BinaryOperator functional interface represents an operation upon two same type operands, producing a same type result as the operands whose functional method is BiFunction.apply(Object, Object). This is a specialization of the BiFunction functional interface for situations where the operands and result are of the same type.
Following is a code sample using the UnaryOperator and BinaryOperator functional interfaces.
package info.java8;
import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;
public class OperatorLambda {
public static void main(String[] args) {
// UnaryOperator
UnaryOperator<Integer> unaryOperatorMethod = a -> (a * a);
System.out.println("Square: " + unaryOperatorMethod.apply(15));
// BinaryOperator
BinaryOperator<Integer> binaryOperatorMethod = (a, b) -> (a * b);
System.out.println("Product: " + binaryOperatorMethod.apply(77, 77));
}
}
Save, compile and run the OperatorLambda class in directory c:\_OOConcepts in the usual way.
OperatorLambda class.The above screenshot shows the output of running our OperatorLambda class.
Predicate and BiPredicate Examples Top
The Predicate functional interface represents a predicate (boolean-valued function) of one argument whose functional method is test(Object).
The BiPredicate functional interface represents a predicate (boolean-valued function) of two argumenta whose functional method is test(Object, Object). This is a two-arity specialization of the Predicate functional interface.
Following is a code sample using the Predicate and BiPredicate functional interfaces.
package info.java8;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
public class PredicateLambda {
public static void main(String[] args) {
// Predicate
Predicate<String> predicate = (s1) ->
{
return s1.equals("two");
};
System.out.println(predicate.test("2"));
System.out.println(predicate.test("two"));
// BiPredicate
BiPredicate<String, String> biPredicate = (s1, s2) ->
{
return s1.equals(s2);
};
System.out.println(biPredicate.test("2", "2"));
System.out.println(biPredicate.test("2", "two"));
}
}
Save, compile and run the PredicateLambda class in directory c:\_OOConcepts in the usual way.
PredicateLambda class.The above screenshot shows the output of running our PredicateLambda class.
Supplier Example Top
The Supplier functional interface represents a supplier of non-specific results whose functional method is get().
Following is a code sample using the Supplier functional interface.
package info.java8;
import java.util.function.Supplier;
public class SupplierLambda {
public static void main(String[] args) {
// Supplier
Supplier<Double> supplier = () -> Math.random();
System.out.println(supplier.get());
System.out.println(supplier.get());
}
}
Save, compile and run the SupplierLambda class in directory c:\_OOConcepts in the usual way.
SupplierLambda class.The above screenshot shows the output of running our SupplierLambda class.
Functional From Java8 Top
With the advent of Java8 several well used interfaces were converted into functional interfaces, these being ActionListener, Callable, Comparator and Runnable. Using lambdas allows
us to interact with these interfaces wihout the use of anonymous classes. This cuts down on code bloat and also makes the code much easier to read.
ActionListener Example Top
The ActionListener functional interface represents a listener interface for receiving action events whose functional method is actionPerformed(ActionEvent).
Following is some code highlighting usage of the ActionListener interface in Java8 and the old style using an anonymous class. As you can see the
lambda expressions are much more concise and easy to read and the expression lambda even fits on one line!
package info.java8;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ActionListenerLambda extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new ActionListenerLambda().setVisible(true));
}
public ActionListenerLambda() {
super("ActionListener Lambda Example");
getContentPane().setLayout(new FlowLayout());
JButton button = new JButton("Click Me!");
getContentPane().add(button);
// Anonymous Class
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.out.println("Handled by Anonymous Class");
}
});
// Expression Lambda
button.addActionListener(e -> System.out.println("Handled by Expression Lambda"));
// Statement Lambda
button.addActionListener(e -> {
System.out.println("Handled by Statement Lambda");
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 100);
setLocationRelativeTo(null);
}
}
Save, compile and run the ActionListenerLambda class in directory c:\_OOConcepts in the usual way.
ActionListenerLambda class.The above screenshot shows the output of running our ActionListenerLambda class and clicking on the Click Me! button.
Callable Example Top
The Callable functional interface represents a task that returns a result and may throw an exception whose functional method is call().
Following is some code highlighting usage of the Callable interface in Java8 and the old style using an anonymous class. As you can see the
lambda expression is much more concise and easy to read and even fits on one line!
package info.java8;
import java.util.concurrent.Callable;
public class CallableLambda {
public static void main(String[] args) {
// Anonymous class example
Callable<String> c1 = new Callable<String>() {
@Override
public String call() {
return "Runnable with Anonymous Class";
}
};
// Expression Lambda
Callable<String> c2 = () -> "Runnable with Lambda Expression";
try {
System.out.println(c1.call());
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
System.out.println(c2.call());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Save, compile and run the CallableLambda class in directory c:\_OOConcepts in the usual way.
CallableLambda class.The above screenshot shows the output of running our CallableLambda class.
Comparator Example Top
The Comparator functional interface represents a comparison function, which imposes a total ordering on some collection of objects.
Following is some code highlighting usage of the Comparator interface using an anonymous class prior to Java8 and using lambda epression from Java8 onwards. As you can see the
expression lambda is much more concise and easy to read and even fits on one line!
package info.java8;
import java.util.Comparator;
import static java.lang.Long.compare;
public class ComparatorLambda {
public static void main(String[] args) {
// Anonymous class example
Comparator<Integer> c1 = new Comparator<Integer>() {
@Override
public int compare(Integer i1, Integer i2) {
return Integer.compare(i1, i2);
}
};
System.out.println(c1.compare(3, 5));
System.out.println(c1.compare(5, 3));
// Expression Lambda
Comparator<Integer> c2 = (i1, i2) -> compare(i1, i2);
System.out.println(c2.compare(3, 5));
System.out.println(c2.compare(5, 3));
}
}
Save, compile and run the ComparatorLambda class in directory c:\_OOConcepts in the usual way.
ComparatorLambda class.The above screenshot shows the output of running our ComparatorLambda class.
Runnable Example Top
The Runnable functional interface represents a task that returns void and whose functional method is run(). The Runnable functional interface should be implemented by any class whose instances are intended to be executed by a thread.
Following is some code highlighting usage of the Runnable interface using an anonymous class prior to Java8 and using lambda epression from Java8 onwards. As you can see the
lambda expressions are much more concise and easy to read and the expression lambda even fits on one line!
package info.java8;
public class RunnableLambda {
public static void main(String[] args) {
// Anonymous class example
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Runnable with Anonymous Class");
}
};
// Statement Lambda
Runnable r2 = () -> {
System.out.println("Runnable with Lambda Statement");
};
// Expression Lambda
Runnable r3 = () -> System.out.println("Runnable with Lambda Expression");
new Thread(r1).start();
new Thread(r2).start();
new Thread(r3).start();
}
}
Save and compile and run the RunnableLambda class in directory c:\_OOConcepts in the usual way.
RunnableLambda class.The above screenshot shows the output of running our RunnableLambda class.
Related Quiz
OO Concepts Quiz 14 - Lambda Expressions
Lesson 14 Complete
In this lesson we looked at lambda expressions which were introduced in Java8, their syntax and how to use them.
What's Next?
In the next lesson we learn about method references, which were also introduced in Java8, their syntax and how to use them.