Java 8 Lambda expressions example

31 May 2014
By Gonçalo Marques
 
java java8
In this article we will cover Java 8 Lambda expressions.

Lambda Expressions

While implementing single method interfaces - often to be passed as a parameter to another function as callbacks - we often end up writing an anonymous class. Considering what we want to achieve, we may come up with something that is unnecessarily verbose.

Let us see an example: The Comparator we pass into Collections.sort

Comparator using an anonymous class

List<Integer> list = Arrays.asList(1, 9, 7, 10, 8);

Collections.sort(list, new Comparator<Integer>() {
  @Override
  public int compare(Integer i1, Integer i2) {
    return i1.compareTo(i2);
  }
});

The anonymous Comparator implementation may be replaced with an equivalent lambda expression:

Comparator using a lambda expression

Collections.sort(list, (i1, i2) -> i1.compareTo(i2));

Lambda expressions may only be used with Interfaces that contain only a single abstract method (a method that is not implemented), also known as functional interfaces. In the concrete case we have just seen, the compiler will know that i1 and i2 are both of Integer type, based on the type of the list we are passing in (see Collections.sort() signature).

Since the method body consists in a single line, we don't need to explicitly write the return keyword. We just need to write the expression being returned right after the arrow token (->).

Let's see another example. Now we will see Spring framework RowMapper:

RowMapper using an anonymous class

List<User> users = jdbcTemplate.query(
  "SELECT USER_ID, USERNAME FROM USER", new RowMapper<User>() {

    @Override
    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
      User user = new User();
      user.setUserId(rs.getLong("USER_ID"));
      user.setUsername(rs.getString("USERNAME"));
      return user;
    }

});

The same concept applies to this example. RowMapper is a single method interface, so we may replace the anonymous class with a lambda expression:

RowMapper using a lambda expression

List<User> users = jdbcTemplate.query(
  "SELECT USER_ID, USERNAME FROM USER", (rs, rowNum) -> {
    User user = new User();
    user.setUserId(rs.getLong("USER_ID"));
    user.setUsername(rs.getString("USERNAME"));
    return user;
});

Based on the mapRow method signature in RowMapper interface, the compiler will know that rs and rowNum are respectively of type ResultSet and int.

The compiler will also infer that the lambda expression will produce a result of type User, that corresponds to each mapped item of the result set. This is because of the type of the variable that we are assigning the query method result into: List<User> users.

This time the method body contains more than a single line, so we must use both the braces ({}) and the return keyword.

Functional Interfaces

As we have said previously, lambda expressions may only be used to implement functional interfaces, ie. interfaces containing only a single abstract method.

Operation.java

@FunctionalInterface
public interface Operation<I, O> {

  O calculate(I input);

}

The @FunctionalInterface annotation is not mandatory, but it will make the compiler force our interface to contain only a single abstract method. If we try to define more than a single abstract method into an interface annotated with @FunctionalInterface, the compiler will show an error.

Based on the interface we just wrote, we may now define lambda expressions like the following:

Lambda expressions example

Operation<Integer, Integer> square = (a) -> a * a;
Operation<Integer, Float> divideByFive = (a) -> a / 5f;
Operation<String, Integer> stringLength = (a) -> a.length();

// Results in 9
int squareOfThree = square.calculate(3);

// Results in 1.8
float nineDividedByFive = divideByFive.calculate(9);

// Results in 5
int helloLength = stringLength.calculate("hello");

Remember that Java 8 also introduced interface default method implementations (see Java 8 Interface Default Methods example). Since these methods are not abstract, we may have methods containing a default implementation along with the single abstract method that defines our functional interface.

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with several years of experience in software development and architecture definition. During this period his main focus was delivering software solutions in banking, telecommunications and governmental areas. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

GitHub profile: https://github.com/gonmarques

He is also the author of the WiFi File Browser Android application: