Java Lambda Expressions


Lambda expressions were introduced in Java 8 and it marks Java’s foray to the functional programming paradigm. It helps produce readable and concise code and can tremendously simplify your Java applications.

Lambda functions are essentially functions — independent units of code that you can use anywhere you would normally use an object, like passing an argument to a method. In this case, instead of creating a class and then creating an object from the class, or directly using an anonymous class, you write your lambda expression and just pass it to the method instead of a class object.

A lambda expression is composed of three parts — function parameters, the -> symbol, and a function body.

  • The first part, function parameters, indicates what arguments you expect your lambda function to take. For instance, if you are iterating over a list of Person objects, you can write the function parameter of the lambda expression as (Person p). If you expect to get two (or more) Person objects as arguments, you would separate them with a comma, (Person p1, Person p2). If you don’t expect any arguments, you simply use empty parentheses (). The rule is that you can omit the parentheses in case your Lambda expression accepts only 1 argument. In all other cases, the parentheses are mandatory.
  • Function parameters are followed by the -> symbol (dash followed by a greater-than symbol).
  • The third component of a lambda expression is a function body, which performs the actual work. You write the function body within curly braces {}. However, if your function body is just one line, you can omit the {} and write it directly after the ->, like so:
Person p -> System.out.println(p.getName());

This is a complete lambda expression. It accepts a Person object as argument and prints its name using the getName() getter.

Stream API

The Stream API was also introduced in Java 8, and can be used to allow chaining of sequential and aggregate operations. Stream operations are either intermediate or terminal in nature.

The stream API isn’t technically a lambda function, but is used in conjunction with lambda expressions to produce concise code.

In this small example you can see that one of the utilities of a stream is to receive a certain property of all objects in a list and return it in another list using intermediate and terminal operations.

Assume you have a Student class like so:

public class Student { int studentId; String studentName; public String getStudentName() { return this.studentName; } public int getStudentId() { return this.studentId; } // setters }

Now assume you’re writing a method which has a list of all the students and you want to get a list of all the student names, which may look something like this:

List<Student> students = /* some list of student objects */; List<String> studentNames = new ArrayList<>(); for(Student student: students) { studentNames.add(student.getStudentName()); }

While this isn’t a bad idea, it can be simplified using streams into just one line of code:

List<Student> students = /* some list of student objects */; List<String> studentNames = students.stream().map(String::getStudentName).collect(Collectors.toList());

The Stream API goes over the list of students and uses the intermediate map function to return a new Stream using whatever method is on the right of the ::.

The terminal collect operation collects the stream as a list of strings.

This is only one use of the Streams API used in Java 8. There are many other applications of streams utilizing other operations as seen here in the
documentation.

Lambda Expressions and Interfaces

Suppose you have an interface that looks something like this:

interface MathInterface { int operation(int x, int y); }

You can create an instance of this interface in one line using lambdas provided that the interface only has one method.

MathInterface multiply = ((int x, int y) -> x * y); MathInterface add = ((x, y) -> x + y); MathInterface subtraction = ((x, y) -> x - y); MathInterface division = (((x, y) -> x / y)); multiply.operation(1, 2); // == 2 add.operation(1, 2); // == 3

Note that in some of these interfaces, we don’t specify the type of each parameter. This is valid and will still work the same as specifying the types such as (int x, int y) -> x * y. If you specify one type, however, you must specify all types of the lambda function.

More Information:

This article needs improvement. You can help improve this article. You can also write similar articles and help the community.