Java 8 Lambdas

Background
Lambdas, which were introduced in Java 8, are a different type of programming construct than most others in Java. That's largely due to the fact that they are a type of functional programming (which is itself, a type of declarative programming), while most of the rest of Java is object-oriented and procedural, which are both types of imperative programming. On first glance, this makes it difficult to understand the syntax of a lambda, but fortunately, it isn't hard to break the syntax down and compare to a known structure to understand what you're looking at when you see a lambda out in the wild.
So what is a Lambda, exactly?
Great question!
Lambda Definition
A lambda expression, which is based on Lambda Calculus, is fundamentally nothing more than an anonymous function (method). It is just a shorter way of writing the implementation of a method for later execution.
Some of the potential benefits of using lambdas include:
- Code reuse
- More concise code
- More readable code
- Opportunities for integration with other APIs such as the Stream API and Collections API
- Potentially smaller compiled code footprint (fewer class files when working with certain classes)
Syntax
So what does a lambda look like?
An example of a simple lambda is shown below
(x) -> x * 2
This lambda takes a value, then returns twice that value.
But where does that syntax come from?
Let's take a look at a simple Java method and see where this lambda is derived from.
Starting with this method, which just takes an int and returns an int...
public int doubleMe(int x) {
return x * 2;
}
...let's delete the access modifier, as we don't need it - all lambdas are public...
int doubleMe(int x) {
return x * 2;
}
...now, since this is a lambda, we don't need the return type either...
doubleMe(int x) {
return x * 2;
}
...since this is an anonymous function, we don't need a method name either...
(int x) {
return x * 2;
}
...this just leaves the formal parameters, along with the method body at this point...
(int x) { return x * 2; }
...with lambdas, we normally don't need to specify the formal parameter type, as it's inferred from the context in which the lambda lives...
(x) { return x * 2; }
...we also don't need the braces, since this is a one line lambda (multi lines lambdas still need the braces {} )...
(x) return x * 2;
...this is a single line lambda, so normally, we don't need the trailing semicolon either...
(x) return x * 2
...and lastly, we don't need the return statement in this case, as it's implied by the context that the lambda is run in...
(x) x * 2
...now we just need a way to differentiate between our formal parameter and the body of the lambda, so we'll add the arrow operator...
(x) -> x * 2
...and just like that, we have our original lambda again!
Types of Lambdas in Java
In Java, we need a way of representing lambdas as objects - it is an object-oriented language after all, so Java provides several classes that represent various types of lambdas. Let's take a look at each of these basic types.
Function
This type of lambda takes a value and returns a value. We've already seen one of these...
(x) -> x * 2
If we were to declare this lambda as a type of Function, it would look like this...
Function<Integer, Integer> function = (x) -> x * 2;
This Function takes an Integer as a formal parameter type, as specified in it's generic parameter declaration (the first generic type declaration) and returns an Integer (the second generic type declaration). The semicolon is needed, in this case, because we aren't passing the lambda as a parameter to a method, but declaring it as a type of Function.
Consumer
This type of lambda takes a value and returns nothing. It's the equivalent of a function with a void return type.
(s) -> System.out.println(s)
In this case, we're taking in some string and just printing it out.
If we declare a Consumer, it would look something like this...
Consumer<String> consumer = (s) -> System.out.println(s);
This Consumer declares a generic type of String, which is how Java is able to figure out the lambda is accepting a String.
Predicate
Predicates are an easy class to understand. They represent some boolean condition, either true or false, so the Predicate class is a type of lambda that takes a value and returns a boolean.
(i) -> i > 5
In this case, we're just testing whether i is less than 5 and returning a boolean value to indicate this.
If we declare this as a Predicate class, it would look something like...
Predicate<Integer> predicate = (i) -> i > 5;
Supplier
The Supplier type of lambda is probably the least used, but is still a valuable tool in your list of lambda types.
This lambda doesn't have any formal parameters, but returns some value. It's useful for generating things like number sequences or random numbers.
() -> new Random().nextInt()
In this case, we're just generating a random int and returning it to the calling process.
If we look at the Supplier declaration, we'll see the Integer type declared in the Supplier generic parameter.
Supplier<Integer> supplier = () -> new Random().nextInt();
Conclusion
Lambdas are a very useful functional construct used to pass method functionality as a formal parameter to another method. They are extremely useful when combined with other APIs such as the Stream API and Collections APIs.