Getting Started with Lambda Expressions

Published: January 12, 2016


Introduction

Lambda expressions are one of the most significant additions in Java 8. They allow you to write more concise and expressive code by treating functionality as a method argument. This tutorial will help you understand the basics of lambda expressions, including their syntax and usage. We will start from simple examples and progressively build up to more advanced scenarios.


What Are Lambda Expressions?

Lambda expressions provide a clear and concise way to represent a method using an expression. A lambda expression allows you to pass behavior as an argument to other methods.

The basic syntax of a lambda expression is:

(parameters) -> expression
  • Parameters: These are the input parameters for the lambda expression. They can be zero or more.
  • Arrow token (->): Separates parameters from the body of the lambda.
  • Expression: The body of the lambda, where the code is executed.

Basic Syntax of Lambda Expressions

Let’s break down the syntax further with some simple examples:

No Parameters:

Runnable greet = () -> System.out.println("Hello, World!");
greet.run();  // Output: Hello, World!

Here, we define a lambda that takes no parameters and prints a message. The lambda is assigned to a Runnable variable, and when run() is called, it executes the lambda.

One Parameter:

Consumer<String> greetName = name -> System.out.println("Hello, " + name);
greetName.accept("Alice");  // Output: Hello, Alice

In this example, we have a lambda that accepts one parameter (name) and prints a greeting message. We use the Consumer functional interface that takes one input argument and performs an action.

Multiple Parameters:

BinaryOperator<Integer> add = (a, b) -> a + b;
System.out.println(add.apply(5, 3));  // Output: 8

Here, the lambda expression adds two integers. We use the BinaryOperator functional interface, which represents an operation on two operands of the same type, returning a result of the same type.


Why Lambda Expressions?

Before Java 8, when you needed to pass behavior to a method (for example, when sorting a list), you would typically use anonymous inner classes. Here’s an example:

// Using anonymous class (before Java 8)
List<String> names = Arrays.asList("Alice", "Bob", "John");
Collections.sort(names, new Comparator<String>() {
    public int compare(String s1, String s2) {
        return s1.compareTo(s2);
    }
});

This approach is verbose and can lead to boilerplate code. Lambda expressions provide a much more concise and readable alternative:

// Using lambda expression (Java 8)
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));

Lambda expressions allow you to pass functionality as parameters to methods in a much more elegant way.


Lambda Expression as Method Parameters

One of the key uses of lambda expressions is passing them as arguments to methods. Many Java 8 API methods accept lambda expressions, such as methods in the Stream API.

List<String> names = Arrays.asList("Alice", "Bob", "John");
names.forEach(name -> System.out.println("Hello, " + name));

In this example, we use the forEach() method on a list, passing a lambda expression to perform an action for each element in the list.


Lambda Expression with Collections

Lambda expressions work seamlessly with collections in Java 8, especially when combined with the new Streams API. Let’s take a look at how we can use lambdas to process a collection of items.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// Using a lambda expression to sum the numbers
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
System.out.println("Sum: " + sum);  // Output: Sum: 15

In this example, we use a lambda expression with the mapToInt() method to convert the list of integers into a stream and then calculate the sum.


Common Functional Interfaces

Java 8 introduced several functional interfaces that work well with lambda expressions. Some of the most commonly used ones include:

  • Runnable: Represents a task that can be executed (takes no arguments).
  • Consumer<T>: Takes a parameter and returns nothing.
  • Supplier<T>: Takes no parameters and returns a result.
  • Function<T, R>: Takes a parameter of type T and returns a result of type R.
  • Predicate<T>: Takes a parameter and returns a boolean value.

Summary

In this tutorial, we introduced lambda expressions, which allow you to express functionality as parameters in a more concise and readable way. We also looked at common use cases of lambda expressions, such as with collections and as method parameters.

Java 8’s introduction of lambda expressions made Java programming more functional, reducing boilerplate code and increasing productivity. As we move forward in the following tutorials, we will see how lambda expressions work together with other Java 8 features like the Streams API.


Conclusion

Lambda expressions are a powerful feature of Java 8, providing a more functional programming style. By using lambdas, you can simplify your code, reduce boilerplate, and write more expressive programs. In the next tutorial, we’ll explore Functional Interfaces and how they work with lambda expressions.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *