Java Lambda Expressions

--

In this article, we’ll go through what Lambda Expressions are in Java and how they work.

1. What is Lambda in Java?

Lambda expressions are short functions that accept zero, one, or more parameters and return a value. This value is usually an object of type Function<T, R>, Consumer<T>, Supplier<T>, etc.

If we compare java methods and java 8 lambda expressions we can notice these differences:

  • Lambda expressions may or may not have a name while methods always have a name.
  • You can not pass methods as a parameter to another method, however, you can pass a lambda expression as a parameter.

Lastly, lambda expressions are one way to implement a Functional Interface. Note that both lambda expressions and functional interfaces were introduced in Java 8.

2. Java Lambda Expression Syntax

In this section, we’ll go through all possible ways to write a lambda expression in Java.

In general, lambda consists of 3 parts:

The parameters part :

// No parameter
()
// One parameter
(var1) // Parenthesis are optional when we have only one parameter
// More than one parameters
(var1, var2, ..., varn)

The arrow part — a dash and a greater than sign(->)

and finally, the implementation part, which is just a snippet of code, surrounded by {}. In case the snippet is just one line of code, the {} can be omitted.

So a complete lambda expression could look like this:

(var1, var2, ..., varn) -> {
lines_of_code;
....
return a_value;
}

3. How to Use Lambda Expression in Java

In this section, we’ll explain all the possible variations of lambda expressions in great detail. Then, we will see the alternative way of writing lambdas.

3.1 One-line Lambda Expression

In the following snippet, you can observe all the possible syntaxes that a one-line lambda expression can have:

// A Lambda that accepts no parameters and returns nothing
Runnable runnable = () -> System.out.println("Hello World");
// A Lambda that accepts no parameters and returns an empty string
Supplier<String> emptyStringFunction = () -> "";
// A Lambda that accepts one parameter and returns nothing
Consumer<String> printString = (aString) -> System.out.println(aString);
// When we have only one parameter, the parenthesis are optional
// so the above can be written as
Consumer<String> printString = aString -> System.out.println(aString);
// At least two parameters lambda that returns nothing,
// and just prints the 2 strings concatenated
BiConsumer<String, String> printStringsConcatenated = (s1, s2) -> System.out.println(s1 + s2);
// At least two parameters lambda expression that returns two concatenated strings
BinaryOperator<String> concat = (s1, s2) -> s1 + s2;
// A 4-parameters lambda that returns a value could be written as follows:
(s1, s2, s3, s4) -> s1 + s2.toUpperCase() + s3.toLowerCase() + s4;

3.2 Multiple-line Lambda

In the following snippet, you can observe all the possible syntaxes that a multiple-line lambda expression can have:

// A Lambda that accepts no parameters and returns nothing
() ->
{
System.out.println("Hello World");
System.out.println("Code Learn Hub");
};
// A Lambda that accepts no parameters and returns an empty string
Supplier<String> emptyStringFunction = () ->
{
System.out.println("Hello World");
return "";
};
// A Lambda that accepts one parameter and returns nothing
Consumer<String> printString = aString ->
{
aString = aString.toUpperCase();
System.out.println(aString);
};
// At least two parameters lambda that returns nothing,
// and just prints the 2 strings concatenated
BiConsumer<String, String> printStringsConcatenated = (s1, s2) ->
{
System.out.println("Inside Lambda");
System.out.println(s1 + s2);
};
// At least two parameters lambda expression that returns two concatenated strings
// With "Hello" at the end
BinaryOperator<String> concat = (s1, s2) ->
{
String sNew = s1 + s2;
sNew = sNew.concat("Hello");
return sNew;
};

3.3 Method Reference

In some cases, we can replace the lambda expressions with a Method Reference. But what is a method reference?

Method reference allows you to write lambda expressions in a more concise way, by referencing a method.

The basic syntax of method reference is the following:

Class_or_object::a_method

Let’s rewrite the previous lambdas using method reference:

Consumer<String> printString = (aString) -> System.out.println(aString);
// Can be written as
Consumer<String> printString = System.out::println;

See? This was pretty easy, but how can we use the method reference for the following?

BiConsumer<String, String> printStringsConcatenated =
(s1, s2) -> System.out.println(s1 + s2);

As there isn’t any function in any class that prints 2 strings concatenated, if we want to rewrite it as a method reference we need to create a method like the following:

private static void printConcatenatedStrings(String s1, String s2){
System.out.println(s1 + s2);
}

Now let’s say this function lives inside a class named LambdaExpression. Therefore, we can rewrite the previous lambda expression as shown below:

BiConsumer<String, String> printStringsConcatenated = 
LambdaExpression::printConcatenatedStrings;

The last lambda expression that we want to rewrite is the following:

BinaryOperator<String> concat = (s1, s2) -> s1 + s2;

Luckily, there is already a function inside the String class that does the exact same thing, that method is String.concat(); so to rewrite the expression above, we need to write the following:

BinaryOperator<String> concat = String::concat

Consequently, we can replace our lambda expression with a method reference as long as the signature of the lambda matches the signature of the method that we will refer to.

This means that for lambda expressions that accept two or more parameters, the one parameter can match the object that calls this method( as happened above with String::concat) or it can be declared explicitly as in the example with the printConcatenatedStrings we used above.

3.4 Constructor Reference

Constructor reference allows us to refer to a constructor, in the same way, we did with the method references. The syntax of the constructor reference is the following:

A_class::new

Let’s jump straight to an example:

public class Person {

private String name;
private int age;

public static void main(String[] args) {
Supplier<Person> personWithName = Person::new;
}

}

In the example above, we created a Supplier that accepts no parameters and returns a new Person object using the empty constructor.

Some important limitations of constructor reference:

  1. You can only use no-parameter constructors with constructor reference, otherwise, you will have to stick to lambda expressions.
  2. You need to use the Supplier Functional Interface if you do want to use a constructor reference

4. Why should you care about Java Lambda Expressions?

Lambda Expressions are used extensively since Java 8 introduced Functional Interfaces, Functions, Optionals, and Stream API. In order to use all of these, you will need to provide functions, that must be written as lambda expressions, method references, or constructor references. If you are interested in how lambda expressions work, below is a list of all lambda expressions related articles:

5. Conclusion

By now you should know how to use lambda expressions and make your code more concise and readable. You can find the source code on my GitHub page.

--

--

Georgios Nikolaos Palaiologopoulos

Experienced Java Developer | Focused on Backend Software Development with Java & Spring Boot | Technical Writer