Why We Need Lambda Expressions in Java - Part 1
Lambda expressions are coming to Java 8 and together with Raoul-Gabriel Urma and Alan Mycroft I started writing a book on this topic.
Join the DZone community and get the full member experience.
Join For FreeLambda expressions are coming to Java 8 and together with Raoul-Gabriel Urma and Alan Mycroft I started writing a book on this topic. Anyway apparently they are still encountering some resistance and not all Java developers are convinced of their usefulness. In particular they say that it could be a mistake to try to add some functional features to Java, because they fear that this could compromise its strong object oriented and imperative nature. The purpose of this article is to hopefully remove any further doubt and show clearly, with practical and straightforward examples, why it is not possible for a modern programming language to not support lambda expressions.
External vs. internal iteration
Let's start with something very simple, a list of integers:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
and a for cycle that iterates all the items in the list and prints them:
for (int number : numbers) {
System.out.println(number);
}
Straightforward as much as common: I don't remember a single day of my more than decennial working life as Java developer when I haven't write at least one cycle like this. Simple as much as ... completely wrong. It remembers me a lot how my 2 years old daughter Sofia puts away her toys after having played with them. It goes on more or less in this way:
Me: "Sofia, let's put the toys away. Is there a toy on the ground"
Sofia: "Yes, the ball"
Me: "Ok, put the ball in the box. Is there something else?"
Sofia: "Yes, there is my doll"
Me: "Ok, put the doll in the box. Is there something else?"
Sofia: "Yes, there is my book"
Me: "Ok, put the book in the box. Is there something else?"
Sofia: "No, nothing else"
Me: "Fine, we are done"
This is exactly what we do everyday with our Java collections, but unfortunately the biggest part of us is not 2 years old. We iterate the collection externally, explicitly pulling out and processing the items one by one. It would be far better for me if I could tell to Sofia just: "put inside the box all the toys that are on the ground". There are two other reasons, why an internal iteration is preferable: first Sofia could choose to take at the same time the doll with one hand and the ball with the other and second she could decide to take the objects closest to the box first and then the others. In the same way using an internal iteration the JIT compiler could optimize it processing the items in parallel or in a different order. These optimizations are impossible if we iterate the collection externally as we are used to do in Java and more in general with the imperative programming.
So, why don't we iterate internally? I think this is only a bad mental habit caused by the lack of support of this pattern in the Java Collection Framework that in turn has been caused by the verbosity (creation of an anonymous inner class) that this implies in pre-8 Java. Something like this:
numbers.forEach(new Consumer<Integer>() {
public void accept(Integer value) {
System.out.println(value);
}
});
Actually both the forEach method and the Consumer interface have been added in Java 8, but you can already do something very similar in Java 5+ using libraries like guava or lambdaj . However Java 8 lambda expressions allow to achieve the same result in a less verbose and more readable way:
numbers.forEach((Integer value) -> System.out.println(value));
The lambda expression is made of two parts the one on the left of the arrow symbol (->) listing its parameters and the one on the right containing its body. In this case the compiler automatically figures out that the lambda expression has the same signature of the only non implemented method of the Consumer interface (that for this reason is called a functional interface) and treat the first as it was an instance of the second, even if the generated bytecode could potentially be different. The declaration of the types of the lambda expression arguments can be, in the biggest part of cases, inferred by the compiler and then omitted as it follows:
numbers.forEach(value -> System.out.println(value));
But we can rewrite this last statement even more concisely using a method reference, another feature introduced in Java 8. More in details in Java 8 it is possible to reference both a static and an instance a method using the new :: operator as in:
numbers.forEach(System.out::println);
In this way, with a process that in functional programming is known as eta expansion, the name of the method is "expanded" by the compiler in the method itself that, as we have already seen, has the same signature of the only abstract method of the Consumer functional interface and then can be in turn converted in an instance of it.
Passing behaviors, not only values
What we have seen in the former example is the main and possibly the only reason why lambda expressions are so useful. Passing a lambda expression to another function allow us to pass not only values but also behaviors and this enable to dramatically raise the level of our abstraction and then project more generic, flexible and reusable API. Let's reenforce this with a further example: starting with the usual list of Integer
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
we are requested to write a method that sums all the Integers in the list as for instance:
public int sumAll(List<Integer> numbers) {
int total = 0;
for (int number : numbers) {
total += number;
}
return total;
}
The day after a manager comes to our cubicle and tells you that the business also requires to have a function that sums only the even number in the list. So what is the quickest thing we could do? Easy. Just copy and paste the former method and add to it the required filtering condition:
public int sumAllEven(List<Integer> numbers) {
int total = 0;
for (int number : numbers) {
if (number % 2 == 0) {
total += number;
}
}
return total;
}
Another day, another requirement: this time they need to sum the numbers in the list again but only if they are greater than 3. So what could we do? Well, we could again copy and paste the former method and just change that boolean condition ... but it feels so dirty, isn't it? Now, following the "First Write, Second Copy, Third Refactor" principle it is time to wonder if there is a smarter and more generic way to do this. In this case implementing an higher-order function accepting together with the list also a Predicate (another functional interface added in Java 8) that defines how to filter the numbers in the list itself before to sum them up.
public int sumAll(List<Integer> numbers, Predicate<Integer> p) {
int total = 0;
for (int number : numbers) {
if (p.test(number)) {
total += number;
}
}
return total;
}
In other words we are passing to the method not only the data (the list of numbers) but also a behavior (the Predicate) defining how to use them. In this way we can satisfy all the 3 requirements with a single more generic and then more reusable method:
sumAll(numbers, n -> true);
sumAll(numbers, n -> n % 2 == 0);
sumAll(numbers, n -> n > 3);
In the second part of this article I will show other examples to demonstrate how lambda expressions can make our Java code more readable and concise.
Opinions expressed by DZone contributors are their own.
Comments