Lambda ExpressionsS2C 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 com.server2client;
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.
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 com.server2client;
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.
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 com.server2client;
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.
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 com.server2client;
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.
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 com.server2client;
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.
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 com.server2client;
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.
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 com.server2client;
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.
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 com.server2client;
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.
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 com.server2client;
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.
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.