Java Lambda Expressions

Java Lambda Expressions: A Complete Guide with Examples and Exercises

In Java, lambda expressions were introduced in Java 8 to provide a clear and concise way to represent functional interfaces (interfaces with a single abstract method). Lambdas enable you to write code that is more readable, concise, and expressive, especially when working with collections and streams.

This article will explore the basics of lambda expressions in Java, their syntax, usage, common use cases, and how they fit into Java’s functional programming features. We will also provide exercises to help you master lambda expressions in Java.


Table of Contents

  1. Introduction to Lambda Expressions
  2. Lambda Expression Syntax
  3. Functional Interfaces
  4. Lambda Expressions in Collections
  5. Lambda Expressions with Streams
  6. Common Use Cases for Lambda Expressions
  7. Exercise
  8. Conclusion

1. Introduction to Lambda Expressions

Before the introduction of lambdas, Java relied on anonymous inner classes to pass behavior as arguments to methods. Lambda expressions simplify this by allowing you to pass behavior as an argument in a much more concise and readable way.

Lambda expressions provide the following advantages:

  • Conciseness: They allow you to write cleaner and more compact code.
  • Readability: They make code more expressive, especially when working with functional programming concepts like mapping and filtering.
  • Encapsulation: They simplify the use of functional interfaces, making your code more modular and reusable.

2. Lambda Expression Syntax

The basic syntax of a lambda expression is:

(parameter1, parameter2, ...) -> expression

Where:

  • parameters: These are the input parameters for the lambda. The number of parameters and their types depend on the functional interface method.
  • expression: The body of the lambda expression. It can either be a single statement or a block of code.

Lambda Expression Example

A lambda expression that adds two integers can be written as:

(int a, int b) -> a + b

Full Example with Runnable Interface

public class LambdaExample {
    public static void main(String[] args) {
        // Using lambda expression to create a thread
        Runnable r = () -> System.out.println("Hello from a lambda thread!");
        Thread thread = new Thread(r);
        thread.start();
    }
}

In this example, a lambda expression is used to implement the Runnable interface and execute the run() method.


3. Functional Interfaces

A functional interface is an interface that has only one abstract method. These interfaces can have any number of default or static methods. The java.util.function package contains many built-in functional interfaces, such as Runnable, Callable, Comparator, Function, Predicate, etc.

Lambda expressions can only be used with functional interfaces. Java’s built-in functional interfaces are often used with lambda expressions to simplify the code.

Example of a Functional Interface

@FunctionalInterface
interface MyFunctionalInterface {
    int add(int a, int b);
}

Here, MyFunctionalInterface has a single abstract method add, making it a functional interface. You can now use a lambda expression to implement this interface.

Using the Functional Interface with a Lambda

public class FunctionalInterfaceExample {
    public static void main(String[] args) {
        // Using lambda expression to implement the functional interface
        MyFunctionalInterface adder = (a, b) -> a + b;

        System.out.println("Sum: " + adder.add(5, 10));  // Output: Sum: 15
    }
}

4. Lambda Expressions in Collections

Lambda expressions are particularly useful when working with Java Collections, such as lists, sets, and maps. They allow you to easily iterate, filter, and manipulate data in a more declarative way.

Using Lambda with forEach()

Java 8 introduced the forEach() method in the Iterable interface, which can accept a lambda expression to perform operations on elements of a collection.

import java.util.*;

public class LambdaCollectionExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

        // Using lambda expression to print each name
        names.forEach(name -> System.out.println(name));
    }
}

Filtering with Lambda Expressions

The Stream API introduced in Java 8 also makes extensive use of lambda expressions. It allows you to perform operations such as filtering and mapping in a functional style.

import java.util.*;
import java.util.stream.*;

public class LambdaStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

        // Using lambda to filter even numbers
        List<Integer> evenNumbers = numbers.stream()
                                           .filter(n -> n % 2 == 0)
                                           .collect(Collectors.toList());

        System.out.println(evenNumbers);  // Output: [2, 4, 6, 8]
    }
}

In this example, we use the filter() method of the Stream interface to select only the even numbers from the list using a lambda expression.


5. Lambda Expressions with Streams

The Stream API allows you to work with sequences of data in a functional style. You can use lambda expressions to operate on data in a stream, such as filtering, mapping, or reducing.

Example: Mapping with Lambda Expressions

import java.util.*;
import java.util.stream.*;

public class LambdaMappingExample {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "cherry");

        // Using lambda to map each word to its uppercase version
        List<String> uppercaseWords = words.stream()
                                           .map(word -> word.toUpperCase())
                                           .collect(Collectors.toList());

        System.out.println(uppercaseWords);  // Output: [APPLE, BANANA, CHERRY]
    }
}

In this example, we use the map() method to convert each word in the list to uppercase.

Example: Reducing with Lambda Expressions

The reduce() method is used to combine elements of a stream into a single result.

import java.util.*;
import java.util.stream.*;

public class LambdaReduceExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Using lambda to calculate the sum of the list
        int sum = numbers.stream()
                         .reduce(0, (a, b) -> a + b);

        System.out.println("Sum: " + sum);  // Output: Sum: 15
    }
}

Here, we use reduce() to sum all elements in the list.


6. Common Use Cases for Lambda Expressions

  • Sorting Collections: You can use lambda expressions with the Comparator interface to sort collections like lists and maps.
import java.util.*;

public class LambdaSortingExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

        // Using lambda expression to sort names alphabetically
        names.sort((a, b) -> a.compareTo(b));

        System.out.println(names);  // Output: [Jack, Jane, Jill, John]
    }
}
  • Event Handling: Lambdas are often used in event handling, such as GUI programming or listening to button clicks.
import javax.swing.*;

public class LambdaEventExample {
    public static void main(String[] args) {
        JButton button = new JButton("Click Me");

        // Using lambda expression for event handling
        button.addActionListener(e -> System.out.println("Button clicked!"));

        // Display button
        JFrame frame = new JFrame("Lambda Event Example");
        frame.setSize(300, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(button);
        frame.setVisible(true);
    }
}

7. Exercise

Exercise 1: Filter a List of Strings

Write a program that uses a lambda expression to filter a list of strings and print all strings that have more than 4 characters.

Exercise 2: Implementing a Functional Interface

Create a functional interface called Multiplier with a method multiply(int a, int b) that returns the product of the two integers. Implement it using a lambda expression and test it.


8. Conclusion

Lambda expressions are a powerful feature in Java that make the code more concise, readable, and expressive. They play a key role in Java’s functional programming capabilities, especially when working with collections and streams. By using lambdas, you can write code that is easier to understand and maintain.

In this article, we’ve explored the syntax and usage of lambda expressions, how they interact with functional interfaces, and how they are used with collections and streams. By practicing the exercises, you can build a strong foundation in using lambda expressions effectively in your Java programs.

Leave a Comment

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

Scroll to Top