Java 8 Lambda expressions example
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
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:
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:
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:
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.
@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:
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.