Java Lambda Expressions

Java Lambda Expressions

Java Lambda Expressions

Lambda Expressions are the most remarkable feature added to the Java platform with Java 8. It’s specified in JSR 335 and JEP 126. The very need for this feature is to gain some of the capabilities supplied by functional programming. The main idea behind this concept is to be able to parametrize the functions for subsequent executions of them.

Till Java 8, this could already be simulated via the use of anonymous inner classes with some design patterns like command pattern and functors. However, the adoption of lambda expressions gave way to the direct use of this concept.

1. What is a Lambda Expression?

A lambda expression is a piece of code that is giving an alternative way to the anonymous class to pass the function as a parameter to other subsequent flows of code such as methods, constructors, etc.. In this approach, a function can be referenced with a variable and passed as a reference to be executed in a subsequent flow of code execution.

The structure of a lambda expression is as follows; it is formed of some arguments, followed by an arrow and that’s followed by a code block.

Lambda Expression

Lambda Expression

2. Type of Lambda Expressions; Functional Interfaces

A lambda expression is identified by a special single-method interface called Functional Interface. A functional interface is the target type that’s determined by the compiler and used as the type of the reference to the lambda expression.

This binding of a lambda expression to a functional interface is determined from the context of the lambda expression. That means, the binding of a lambda expression to a target type can take place in different contexts such as variable declarations, method arguments, constructors, etc.; and from this binding, the compiler finds the target type, which is a functional interface, and infers the types of the parameters used in the lambda expression according to that functional interface.

A functional interface can be marked with an informative annotation @FunctionalInterface that can be used to inform other developers.

Let’s do a simple example to understand it well.
Think that we want to lowercase or uppercase a text based on a condition.
It will be a dynamic evaluation so we can abstract the operation.
By leveraging the lambda expressions we can do it as following:

Here is the lambda expressions for case operations:

By looking at the code above, we see that there is a parameter t; we do not know its type, and in the code block of the expression, the methods of t, which are toUpperCase and toLowerCase, are called.

To be able to pass these expressions to somewhere, we have to declare a functional interface; with that single-method interface, the compiler will be able to infer the type of t:

Then we can write our main code as such:

Here, when we call the method printWithCaseOperation with a lambda expression as its second parameter, the compiler infers that the method’s second parameter is of type CaseOperation and, so is also of the lambda expression’s type, too.

3. Some Internals

At this point, let’s watch this video to listen to the internals of lambda expressions. For whom needs some speed, I will summarize it, you’re welcome:

  • We need lambdas basically since of; parallel friendly APIs and less code with the usage of closure-like functional programming capabilities.
  • Lambdas are not a new function type in the VM-level; it’s mostly about compiler level.
  • The compiler does a great job for us by transforming our lambdas into the form of a related defined functional interface. In other words; the compiler infers our lambdas as functional interfaces.
  • When the compiler sees the lambda expression, it simply creates a static method from the resolved related functional interface and in the invocation time, the VM executes that method by calling invokedynamic which is an invocation mode introduced in Java SE 7.

4. Lambda Syntax

Java Lambda Expressions have a basic structure as drawn in the diagram above. Besides this, some inferences can be made automatically by the compiler for us. When writing lambda expressions these compiler inferences directs us to write less code.

4.1 Full Syntax

The full syntax of a lambda expression is as follows.

The left side of the arrow is just like the input type declaration part of a method signature.
Then put the arrow.
And lastly, write the body in curly braces just as in a method body.

4.2 Single Statements in Body

Curly brackets and the return keyword can be omitted in case of single statements in the body.

4.3 Implicit Target Types

Types of the input variables can be omitted and these can be inferred by the compiler.

4.4 Single Implicit Target Type

Parentheses are optional in case of a single implicit target type.

4.5 Explicit Target Types

When using explicit target types, then parentheses are required.

4.6 Lambda Expressions Without Parameters

4.7 Multiple Statements In Body

The body can have multiple statements and in this case, the use of curly braces is mandatory.

5. Built-in Functional Interfaces

Now that we know; ultimately, a functional interface is a method reference and also defines the target type of a lambda expression for the sake of compiler.

So we can structure our API around functional interfaces and use lambdas for more effective and clean code. However, as you see, a functional interface just defines the target types of lambda expressions. Hence, the same functional interfaces could be used in most cases. For that aim, in Java 8; several common built-in functional interfaces have already been created for us.

So instead of declaring our custom functional interfaces, we can use the built-in ones that will mostly meet our needs. Let’s look over that built-in functional interfaces.

5.1 Functions

The Function interface can be used in case of the need for;

      one input, one output

So if you need a functional interface that gets one input and returns an output then you should use the Function interface instead of creating a custom one of yours.

Let’s examine the following code:

In the code above, our lambda gets an instance t of type Person as an input and returns the name as String. When we execute the lambda via the Function interface then we get the result.

5.2 Suppliers

The Supplier interface can be used in case of the need for;

no input, one output

So if you need a functional interface that gets no input and returns an output then you should use the Supplier interface instead of creating a custom one of yours.

Let’s examine the following code:

5.3 Consumers

The Consumer interface can be used in case of the need for;

one input, no output

So if you need a functional interface that gets one input and returns no output then you should use the Consumer interface instead of creating a custom one of yours.

Let’s examine the following code:

5.4 Predicates

The Predicate interface can be used in case of the need for;

one input, one boolean output

So if you need a functional interface that gets one input and returns a boolean output then you should use the Predicate interface instead of creating a custom one of yours.

Let’s examine the following code:

5.5 BiPredicate

The BiPredicate interface can be used in case of the need for;

two inputs, one boolean output

5.6 Primitives version of Predicate

IntPredicate, LongPredicate, and DoublePredicate are primitive versions of Predicate interface.

In these versions; you do not need to declare the input type. For example; for IntPredicate, the input type is an integer value.

5.7 UnaryOperator

The UnaryOperator interface can be used in case of the need for;

one input, one output and both are the same type

Let’s look over the following code:

5.8 BinaryOperator

The BinaryOperator interface can be used in case of the need for;

two inputs, one output and all are the same type

Let’s look over the following code:

6. Composition Support In Built-in Functional Interfaces

Some of the built-in functional interfaces provide some utility methods enabling the composition of multiple functions.

6.1 Predicate Composition

The Predicate interface provides two utility methods for combining the predicates: and, or.
With these methods, we can easily combine existing predicates to generate new ones.

6.1.1 Predicate and

The Predicate interface provides the default method and. With the use of and, the new combined predicate returns true if all of the predicates return true.

Let’s look over the following code:

6.1.2 Predicate or

The Predicate interface also provides the default method or. With the use of or, the new combined predicate returns true if any one of the predicates returns true.

Let’s look over the following code:

6.2 Function Composition

The Function interface provides two utility methods for combining the functions: compose, andThen.
With these methods, we can easily combine existing functions to generate new ones.

6.2.1 Function compose

The Function interface provides the default method compose. With the use of compose, a new function is generated from a chain of functions. The new combined function will operate in the reverse order of the functions chained.

Let’s look over the following code:

6.2.2 Function andThen

The Function interface provides the default method andThen. With the use of andThen, a new function is generated from a chain of functions. The new combined function will operate in the same order of the functions chained.

Let’s look over the following code:

7. Summary

In this article, we have looked over Java lambda expressions and its special reference type, Functional Interfaces. We have also gone over the built-in functional interfaces provided and examined the basic use cases.

In the next step, it will be worthwhile to going further to the details of the Java 8 Stream API.

You can see the sample code for this article on my Github page:
https://github.com/erolhira/java

About the author

Stay Informed

It's important to keep up
with industry - subscribe!

Stay Informed

Looks good!
Please enter the correct name.
Please enter the correct email.
Looks good!

Related articles

9.01.2024

Sending Emails in Java Applications: A Comprehensive Guide to JavaMail and SMTP Configuration

The JavaMail API, part of the Java EE (Enterprise Edition) platform, simplifies the process of sending and receiving emails in Java applications. It ...

15.05.2023

10 Practices You Should Avoid to Become a Good Java Developer

Java is an object-oriented, case sensitive and class based programming language. It strictly follows the concept of OOPs(Object oriented programming ...

8.12.2020

Spring Security Basics

Spring Security is a framework for securing Spring-based applications. In this article, we will look over the core Spring Security ...

No comments yet

Sign in

Forgot password?

Or use a social network account

 

By Signing In \ Signing Up, you agree to our privacy policy

Password recovery

You can also try to

Or use a social network account

 

By Signing In \ Signing Up, you agree to our privacy policy