Lambda Expressions in Java

lambda expressionsfunctional interfacesmethod references

In the previous blog we have learned about the Interface in Java -Part 2. If you want to know more about it, then visit the blog Interface in Java Part 2. In this blog, we will go through the concept of Lambda Expressions. Lambda Expressions were introduced in Java 8. Lambda Expression is a block of code that takes in the parameter and gives value in return. They are the same as methods but do not require a name and can be implemented right in the body of a method. We will go through the concepts in Lambda Expressions:


Lambda Expressions in Java

In the previous blog, in the section Callback using Interfaces in Java we have seen how to do work in a time interval.

e.g.

class TimerDisplay implements ActionListener{
  public void actionPerformed(ActionEvent actionEvent){
    ……
  }
}

If we want to execute the code multiple times, we can create an object of the TimerDisplay class and then that object can be given to the Timer object. The actionPerformed() method defined in the class TimerDisplay contains the code that we want to execute later. Suppose we want to sort the string by length rather than directory order, in this case, we can create an object of Comparator to the sort() method.

e.g.

class LengthComparatorEx implements Comparator<String>{
  public int compare(String firstStr, String secondStr){
    firstStr first.length() - secondStr.length();
  }
}
. . .
Arrays.sort(strings, new LengthComparator());

The compare() method is not called immediate. Rather the sort() method calls the compare() method and rearranges the elements if they are not in the correct order until the array is sorted. In both examples, there is one common thing: The block of code passed to any of them i.e. timer or sort() method. That particular block of code gets called after some time. Until now providing a particular block of code to someone was not an easy job in Java. We cannot just pass the block of code around. As Java is an object oriented language you have to create an object belonging to a particular class that has a method with the desired code. In other languages, it is possible to work with block of code directly. We know that the biggest strength of Java is its consistency and simplicity, but if we each and every feature it will become an unmaintainable mess.


Syntax of Lambda Expression

We will take the same example which we saw in the previous section. We can pass the code to check whether one string is shorter than the other. We can perform it using the following:

e.g.

firstStr.length() - secondStr.length()

In the above statement firstStr and secondStr are two strings. We must specify them as strings.

e.g.

(String firstStr, String secondStr)-> firstStr.length() - secondStr.length()

The above expression is a lambda expression. Such an expression is simply a block of code along with the specification of any variable that can be passed to a code. If the code doesn’t fit in the single expression then we can write it just like the method enclosed with the curly braces() along with the return statement.

e.g.

(String firstStr, String secondStr) ->{
  if (firstStr.length() < secondStr.length()){
    return -1;
  }
  else if (firstStr.length() > secondStr.length()) {
    return 1;
  }
  else return 0;
}

If the lambda expression does not have any parameter we can provide empty parentheses just like with the no parameter method.

e.g.

() -> { for (int i =0; i <=20; i++) System.out.println(i); }

If the parameter types of a lambda expression is inferred, then we can omit them.

e.g.

Comparator<String> comparaterobj
  = (firstStr, secondStr)
  -> firstStr.length() - secondStr.length();

The compiler can omit the firstStr in the preceding statement, and the secondStr must be a string because the lambda expression is assigned to the string comparator. If a method has a single parameter with inferred type, we can even omit the parentheses

e.g.

ActionListener listenerObj = event ->
System.out.println("The time is:"+ Instant.ofEpochMilli(event.getWhen()));
-> . . .

e.g.

import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class LambdaExpressionEx{
  public static void main(String[] args){
    var days = new String[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
    System.out.println(Arrays.toString(days));
    System.out.println("
Sorted in dictionary order:");
    Arrays.sort(days);
    System.out.println(Arrays.toString(days));
    System.out.println("
Sorted by length:");
    Arrays.sort(days, (firstStr, secondStr) -> firstStr.length() - secondStr.length());
    System.out.println(Arrays.toString(days));
    var timerVar = new Timer(1000, event ->
    System.out.println("
The time is " + new Date()));
    timerVar.start();
    JOptionPane.showMessageDialog(null, "Exit program?");
    System.exit(0);
  }
}

Output

[Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday]
Sorted in dictionary order:
[Friday, Monday, Saturday, Sunday, Thursday, Tuesday, Wednesday]
Sorted by length:[Friday, Monday, Sunday, Tuesday, Saturday, Thursday, Wednesday]
The time is Sat Jun 18 19:08:56 GMT 2022
The time is Sat Jun 18 19:08:57 GMT 2022
The time is Sat Jun 18 19:08:58 GMT 2022
The time is Sat Jun 18 19:08:59 GMT 2022
The time is Sat Jun 18 19:09:00 GMT 2022The time is Sat Jun 18 19:09:01 GMT 2022
The time is Sat Jun 18 19:09:02 GMT 2022
The time is Sat Jun 18 19:09:03 GMT 2022
The time is Sat Jun 18 19:09:04 GMT 2022
The time is Sat Jun 18 19:09:05 GMT 2022
The time is Sat Jun 18 19:09:06 GMT 2022The time is Sat Jun 18 19:09:07 GMT 2022
The time is Sat Jun 18 19:09:08 GMT 2022
The time is Sat Jun 18 19:09:09 GMT 2022
The time is Sat Jun 18 19:09:10 GMT 2022
The time is Sat Jun 18 19:09:11 GMT 2022
The time is Sat Jun 18 19:09:12 GMT 2022
The time is Sat Jun 18 19:09:13 GMT 2022
The time is Sat Jun 18 19:09:14 GMT 2022
The time is Sat Jun 18 19:09:15 GMT 2022

Functional Interfaces in Java 8

There are many existing Interfaces in Java that encapsulates the block of code such as ActionListner or Comparator. Lambdas are compatible with these interfaces. Whenever an object of an interface with a single abstract method is expected then we can provide a lambda expression. This is called a functional interface. Let's consider an example of Array.sort method. In Array.sort method the second parameter requires an object of Comparator, an interface with a single method.

e.g.

Arrays.sort(words,(firstStr, secondStr) -> firstStr.length() - secondStr.length());

The java.util.function package defines various general functional interfaces defined by the Java API. One of them is BiFunction <T,U,R> which describes the function with parameter type T and U and R return type. We can then save the string comparison lambda in a variable of that type.

e.g.

BiFunction<String, String, Integer> compObj= (firstStr, secondStr) -> firstStr.length() - secondStr.length();

The useful interface in the package java.util.function is Predicate

e.g.

public interface Predicate<T>{
  boolean test(T obj);
}

The Array class has a particular method which has the parameter as Predicate i.e.removeIf(). It is designed to pass lambda expression.

listObj.removeIf(e -> e == null);

In the above statement it removes all the null values from an array. Another helpful functional interface is Supplier<T>

e.g.

public interface Supplier<T>{
  T get();
}

Supplier interface doesn’t have any arguments. Supplier interface is used for lazy evaluation.


Method References in Java

Lambda Expressions can involve a single method. Suppose we want to print the object whenever the timer event takes place.

e.g.

var timerObj = new Timer(1000, eventObj -> System.out.println(eventObj));

Rather than passing the event object in the println() method we can just pass the println() method to the Timer constructor.

e.g.

var timerObj = new Timer(1000, System.out::println);

In the above statement System.out::println is a method reference. Another example is if we want to sort a string without the concern of letters, then we can write as:

e.g.

Arrays.sort(string, String::compareToIgnoreCase);

In the above example the:: operator is used as a separator which separates the method name from the name of an object or class. There are three forms of method references:

  1. object::instanceMethod
  2. Class::instanceMethod
  3. Class::staticMethod

In the first form i.e object::instanceMethod, the method reference is similar to a lambda expression in which parameters are passed to the method. Let’s take the example System.out.println, in this the object is System.out, and println() is the method which is similar to the expression:

e.g.

a -> System.out.println(a)

In the second form i.e. Class::instanceMethod the first parameter will act as the implicit parameter of that method. Suppose consider the example String::compareToIgnoreCase is equivalent to :

e.g.

(a, b) -> a.compareToIgnoreCase(b)

In the third form i.e. Class::staticMethod all the parameters are passed to the static method: Math::max(a,b) is similar to :

e.g.

(a, b) -> Math.pow(a, b)

In the method reference we can also use this parameter i.e. this::equals. this::equals is equivalent to

e.g.

a -> this.equals(a)

We can also use super in method reference as follo2ws:

e.g.

super::instanceMethod

The above expression uses this as the target and invokes the superclass of the given method.

Get in Touch

Atrowel will be pleased to receive your feedback and suggestions. Those of you who would like to contribute, please send us an email.