Java Lambda Expressions: Functions as First-Class Citizens
Whether you're migrating to Java 8 or just want a refresher, here's an excellent primer of lambda expressions and how they help bridge OOP and FP in Java.
Join the DZone community and get the full member experience.
Join For FreeSince Java 8, a lot of boilerplate code can be replaced with lambda expressions in our codebase. I really want to put out an article about Streams in Java, but since that carries real value only if we combine them with lambda expressions, I want to write about some ideas about playing around with lambda expressions first.
Functional Interfaces in Java
A functional interface is an interface with a single abstract method, also known as SAM (Single Abstract Method) types. The concept was introduced in JDK 8, but there were interfaces prior to JDK 8 that complied with that definition. For example, Comparator
has only one method: compare()
.
We can create our own as:
@FunctionalInterface
interface Calculator<T,R> {
R calculate(T a, T b);
}
Here, the @FunctionalInterface
annotation is checked by the compiler. So, if the interface does not contain exactly one abstract method, there is a compiler error.
It is not necessary to use this annotation when providing a type for a lambda expression, but, like other annotations (@Override
, for example), it is a best practice to use it because it tells the compiler to check that it would work — otherwise, it will be overlooked until runtime.
Now, What Is a Lambda Expression?
A lambda expression is an inline implementation of a functional interface, eliminating the need of an anonymous class.
A lambda expression has three parts:
parameters [zero or more]
->
code block [if more than one statement, enclosed in curly braces { . . . } ]
[may contain free variables; values for these supplied by local or instance vbles]
For example:
(a,b) -> a + b; // given two numbers a and b, return another number with their summation.
(String str) -> System.out.println(str); // prints the given string into console
The equivalent of the first lambda expression is:
new Calculator<Integer, Integer>() {
@Override
public Integer calculate(Integer a, Integer b) {
return a + b;
}
};
You can see how lambda expressions help to get rid of the boilerplate code.
Naming Lambda Expressions
Every object in Java has a type; the same is true of lambda expressions. The type of a lambda expression is any functional interface for which the lambda expression is an implementation.
Naming a lambda expression is done by using an appropriate functional interface as its type, like naming any other object. For example:
Calculator<Integer, Integer> adder = (a,b) -> a + b;
The name of this lambda expression is adder
of type Calculator
.
Note: I am omitting the parameter type because if parameter types can be inferred, they can be omitted. So, the above expression with the parameter type would be:
Calculator<Integer, Integer> adder = (Integer a,Integer b) -> a + b;
Using Lambda Expressions
You've seen how inner class approximations can be replaced by lambda expressions, which capture their essential functional nature: Arguments mapped to outputs. Hence, the functions are now first-class citizens. So:
1. A lambda expression can be used as a regular class object to call the method. For example, adder
can be used to call the calculate
method as:
Integer sum = adder.calculate(3,4); // sum = 7
2. Lambda expressions can be passed as an argument and used to evaluate expressions as:
// ...
printSum(adder);
// ...
static void printSum(Calculator cal) {
System.out.println(cal.calculate(4,5));
}
3. Lambda expressions can be returned as a return object:
// ...
Calculator<Integer, Integer> multiplier = getCalculatorFunc();
Integer result = multiplier.calculate(6,9)); // 54
// ...
static Calculator getCalculatorFunc() {
Calculator<Integer, Integer> multiplier = (a, b) -> a * b;
return multiplier;
}
Conclusion
Now, you can see how it is possible to reap many of the benefits of Functional Programming while maintaining the OO essence of the Java language as a whole. Lambda expressions are the fundamentals for functional programming and writing code concisely with Streams, which I want to continue in the next post. Until then, happy coding!
The source code for the examples presented above is available on GitHub.
Opinions expressed by DZone contributors are their own.
Comments