Get Started With Lambda Expressions in Java 8
Lambda expressions are the most touted feature of Java 8. They promise to reduce the amount of code you have to write, make your code less error-prone, and make it easier to read. Let’s understand how lambdas manage to do all this using an example.
Filtering a Collection
One of the most common operations on a collection is to filter it based on some criteria. Assuming that you have a collection of movies, you might want to remove the movies that have low ratings. The traditional way to perform this filtering is using an iterator. You loop over all the elements of the collection and remove the ones that you don’t need.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class Main { public static void main(String[] args) { Collection<Movie> movies = new HashSet<>(); movies.add(new Movie("The Shawshank Redemption", 9.2)); movies.add(new Movie("The Dark Knight", 8.9)); movies.add(new Movie("Black Swan", 8.0)); Iterator<Movie> it = movies.iterator(); while (it.hasNext()) { Movie m = it.next(); if (m.getRating() < 8.5) { it.remove(); } } System.out.println(movies); } } |
The Movie
class used in this example is a simple Java POJO.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Movie { private String name; private double rating; public Movie(String n, double r) { this.name = n; this.rating = r; } public double getRating() { return rating; } public String getName() { return name; } @Override public String toString() { return "{" + name + ", " + rating + "}"; } } |
Generalize using an Interface
If you want to filter the collection on multiple criteria, you need to write this kind of iterator/loop based code each time. Most of this code is boilerplate and you’d want to abstract it away. You can create an interface to represent the test you need to execute on each element (it’s generally called a predicate in programming parlance).
1 2 3 |
interface Predicate<T> { boolean test(T t); } |
Using this abstraction, you can write a general method that will be able to filter any collection given a predicate instance
1 2 3 4 5 6 7 8 9 10 11 |
class Collections { public static <T> void removeAll(Collection<T> coll, Predicate<T> pred) { Iterator<T> it = coll.iterator(); while (it.hasNext()) { T t = it.next(); if (pred.test(t)) { it.remove(); } } } } |
Now, you can generalize your original code by making use of anonymous inner classes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import java.util.*; public class Main2 { public static void main(String[] args) { Collection<Movie> movies = new HashSet<>(); movies.add(new Movie("The Shawshank Redemption", 9.2)); movies.add(new Movie("The Dark Knight", 8.9)); movies.add(new Movie("Black Swan", 8.0)); Collections.removeAll(movies, new Predicate<Movie>() { @Override public boolean test(Movie m) { return m.getRating() < 9.0; } } ); System.out.println(movies); } } |
Here comes the lambda
Although this code is much more flexible, it isn’t very intuitive or easy to read. There’s a lot of boilerplate code around the single line that is the main concern here.
1 2 3 4 5 6 7 |
Collections.removeAll(movies, new Predicate<Movie>() { @Override public boolean test(Movie m) { return m.getRating() < 9.0; } } ); |
This is where the lambda expressions feature of Java 8 performs its magic. This same piece of code, when written using lambdas, looks like this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import java.util.*; public class Main3 { public static void main(String[] args) { Collection<Movie> movies = new HashSet<>(); movies.add(new Movie("The Shawshank Redemption", 9.2)); movies.add(new Movie("The Dark Knight", 8.9)); movies.add(new Movie("Black Swan", 8.0)); Collections.removeAll(movies, m -> m.getRating() < 9.0); System.out.println(movies); } } |
Without a doubt, this code is much more concise than before. It is quite obvious what is going on here. You want to remove all elements from the collection movies
where for element m
, the rating is less than 9.0.
Internals
But how does it work internally? From the method signature of removeAll()
, the compiler knows that the second argument is of type Predicate
. Moreover, the Predicate
interface has only one method. That method takes a single argument of type T
and from the context of the call, the compiler knows that T
is bound to type Movie
in this case. Using all this information, it can create an anonymous inner class (similar to our own implementation above) on its own. In a sense, lambda expressions are just “syntactic sugar” to simplify the code.
Update May 30, 2014
Thanks to Mike and Jonathan of the Toronto Java Users Group (and Reddit users jamanifin, neutronbob & nicoulaj) for pointing out that lambdas do not create anonymous inner classes. Mike looked at the byte code that’s generated when using the built-in
java.util.Collection.removeIf
method.With Lambdas:
12 66: invokedynamic #16, 0 // InvokeDynamic #0:test:()Ljava/util/function/Predicate;71: invokeinterface #17, 2 // InterfaceMethod java/util/Collection.removeIf:(Ljava/util/function/Predicate;)ZWithout Lambdas:
1234 66: new #16 // class Main2$169: dup70: invokespecial #17 // Method Main2$1."<init>":()V73: invokeinterface #18, 2 // InterfaceMethod java/util/Collection.removeIf:(Ljava/util/function/Predicate;)ZThis also proves that while lambdas may seem like syntactic sugar, they are definitely much more. Thanks guys.
Default Methods
What happens if the Predicate
interface contains more than one method? The compiler throws up an error saying that Predicate
is not a functional interface. A functional interface is one that has only one abstract method. In other words, if you want to use lambda expressions in your code, make sure that your interface has only one abstract method. If you need to have more methods, then use the “Default Methods” feature of Java 8!
Any chance of a link back to my course I mentioned on Reddit?
https://www.udemy.com/whats-new-in-java-8/?couponCode=20OFF
Thanks
Nice tutorial. By the way, I have also shared few examples on Java 8 lambda expressions, which complements your post. Your reader may find that useful.
Regards
Javin
Do you have a link to the reddit converstation you metnioned?
Sure Anorm, here’s the Reddit conversation I mentioned:
http://www.reddit.com/r/java/comments/26ovuh/get_started_with_lambda_expressions_in_java_8/
Cheers.