Lambda Expressions in Java 1.8

Java 1.8 brings many new features, among them is the ability to use Lambda Expressions or just lambdas but unless you’ve used them before  it’s not clear what benefit they may offer. This post goes through a simple use case to highlight when a lambda bit be useful.

Let’s start with a simple user object, a class with a few fields that represent the user in our system.

public class User {

private String firstName;
private String lastName;
private String title;
private String company;
...

@Override
public String toString() {
    return "User{" +
           "firstName='" + firstName + '\'' +
           ", lastName='" + lastName + '\'' +
           ", title='" + title + '\'' +
           ", company='" + company + '\'' +
           '}';
}

In our imaginary system we have an ArrayList of these users that we would like to filter down. Let’s say for example we’re looking for all users who may have the first name ‘Bruce’ or work for a particular company. One way to do this would be to iterate over the ArrayList and add a each instance that matches our criteria to another list.

List users = UserGenerator.createUsers();

// get users by a company
List usersAtLinkLinks = new ArrayList();
for (User user: users){
    if ("Linklinks".equalsIgnoreCase(user.getCompany())){
        usersAtLinkLinks.add(user);
    }
}
System.out.println("Filtered by company:");
System.out.println(usersAtLinkLinks);


//get users by first name
List usersWithFirstNameBruce = new ArrayList();
for (User user: users){
    if ("Bruce".equalsIgnoreCase(user.getFirstName())){
        usersWithFirstNameBruce.add(user);
    }
}
System.out.println("Filtered by first name:");
System.out.println(usersWithFirstNameBruce);

This isn’t very flexible though, what if we want to add new criteria? We’d have to modify the existing logic or add new methods that duplicate things. One possible improvement would be to use an interface for our filters and provide concrete or anonymous class.

public interface UserFilter {

    List filter(List users, String target);
}

List userList = UserGenerator.createUsers();

// Define a filter to filter on company name
UserFilter companyFilter = new UserFilter() {
    @Override
    public List filter(final List users, final String company) {
        List filteredUsers = new ArrayList<>();
        for (User user : users) {
            if (user.getCompany().equalsIgnoreCase(company)) {
                filteredUsers.add(user);
            }
        }
        return filteredUsers;
    }
};

System.out.println("Filtered by company:");
System.out.println(companyFilter.filter(userList, "Linklinks"));

While this is a bit more structured it’s a little excessive to provide an interface that only describes one function, it also means that if we were to change the signature of that function we would have to re-implement it for every anonymous class. This is a good opportunity for us to use a Lambda expression!

List userList = UserGenerator.createUsers();

System.out.println("Filtered by company:");
System.out.println(userList.stream()
                           .filter(user -> user.getCompany().equalsIgnoreCase("Linklinks"))
                           .collect(Collectors.toList()));

System.out.println("Filtered by first name:");
System.out.println(userList.stream()
                           .filter(user -> user.getFirstName().equalsIgnoreCase("Bruce"))
                           .collect(Collectors.toList()));

In that final example we’re using a lambda to filter the results for us, as it’s an anonymous function we’re not having to deal with the overhead of creating/maintaining an interface and we’re not righting code that’s as difficult to maintain as a simple for loop and if statement. Observe how simple this makes the implementation of a filter!

Let’s look a little more at how the lambda is implemented. The format of the lambda is as follows;

  • A comma separated list of formal parameters in parenthesis. As their is only one parameter above,”user”, then we can omit the parenthesis.
  • The arrow token, ” -> “.
  • The statement, so in the case where we are looking for a specific company, “user.getCompany().equalsIgnoreCase(“Linklinks”)”. As we are using a single expression, the Java runtime is evaluating it then returning the value.

We’re also using some stream functionality that is also in Java 1.8, see the following post for more information on what “stream()”, “.filter” and “.collect” are doing.

There is a caveat to consider when using lambdas, it’s very easy for them to become very complicated, particularly when you have a fair bit of logic in the statement block. When looking at using a lambda, consider how readable it is, would someone with limited knowledge of their use be able to follow what you’ve done?

Lastly, if you were to write something like the first example, IDEs like IntelliJ will probably warn you that you could simplify it down to a lambda like this if you’re project is configured to use Java 1.8. Very useful!

Hopefully this post has helped you gain a better understanding of why you would want to use a lambda and how to do so!

Resources:

The syntax for Lambda expressions is available in the Oracle documentation.

The data used for my examples was generated using mockaroo.

The full example used by the tutorial is available here on GitHub.

A good explanation of what stream provides.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s