Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Java Lambda Expressions: Functions as First-Class Citizens

DZone's Guide to

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.

· Java Zone ·
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

Since 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.

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
java ,java 8 ,lambda expressions ,functional interface ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}