-->

Java - Lambda Expressions

1. What are Lambda Expressions?

A lambda expression is a short block of code that takes in parameters and returns a value. It can be used as a concise way to represent instances of functional interfaces (interfaces with a single abstract method).

Before Java 8, using anonymous classes was the standard way to pass behaviour to methods, but lambda expressions provide a more elegant and readable alternative.

Example:

// Traditional way using an anonymous class

Runnable runnable = new Runnable() {

    @Override

    public void run() {

        System.out.println("Hello, World!");

    }

};

// Using a lambda expression

Runnable runnableLambda = () -> System.out.println("Hello, World!");

2. Why Use Lambda Expressions?

Benefits of Lambda Expressions:

Conciseness: Reduces boilerplate code, making your codebase cleaner.

Readability: Makes the code easier to understand, especially for simple operations.

Functional Programming: Enables a functional programming style in Java, leading to more declarative code.

Better Use of Collections API: Works seamlessly with Java Streams and functional-style operations like map, filter, and reduce.

3. Syntax of Lambda Expressions

The syntax of a lambda expression is simple and consists of three parts:

(parameterList) -> { body }

Examples of Different Lambda Syntaxes:

No Parameters:

() -> System.out.println("Hello, Lambda!");

Single Parameter (Parentheses Optional):

name -> System.out.println("Hello, " + name);

Multiple Parameters:

(a, b) -> a + b;

With a Block of Code:

(int x, int y) -> {

    int sum = x + y;

    return sum;

};

4. Functional Interfaces

A functional interface is an interface with exactly one abstract method. These interfaces are used as the types for lambda expressions. Java 8 provides several built-in functional interfaces in the java.util.function package.

Examples of Functional Interfaces:

Runnable: void run()

Callable<T>: T call()

Comparator<T>: int compare(T o1, T o2)

Predicate<T>: boolean test(T t)

Function<T, R>: R apply(T t)

Consumer<T>: void accept(T t)

Supplier<T>: T get()

Example:

@FunctionalInterface

interface MyFunctionalInterface {

    void display();

}

// Using Lambda Expression

MyFunctionalInterface message = () -> System.out.println("Hello Functional Interface!");

message.display();

5. Examples of Lambda Expressions

5.1. Using Lambdas with Collections

Sorting a List:

List<String> names = Arrays.asList("John", "Alice", "Bob");

// Using Lambda Expression to sort

names.sort((s1, s2) -> s1.compareTo(s2));

System.out.println(names);

Filtering a List:

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

// Using Lambda Expression to filter even numbers

numbers.stream()

       .filter(n -> n % 2 == 0)

       .forEach(System.out::println);

5.2. Using Lambdas with Streams

The Streams API leverages lambda expressions to provide a functional approach to processing sequences of elements.

Example: Map and Filter with Streams

List<String> names = Arrays.asList("John", "Jane", "Jack", "Doe");

// Using Streams and Lambdas to filter and map

names.stream()

     .filter(name -> name.startsWith("J"))

     .map(String::toUpperCase)

     .forEach(System.out::println);

Output:

JOHN

JANE

JACK

6. Method References

Method references are a shorthand notation of lambdas to call methods. They provide a more readable and concise way to use lambdas.

Types of Method References:

Reference to a Static Method: ClassName::methodName

Reference to an Instance Method: object::methodName

Reference to a Constructor: ClassName::new

Example:

List<String> names = Arrays.asList("John", "Alice", "Bob");

// Using Method Reference

names.forEach(System.out::println);

7. Conclusion

Lambda expressions are a game-changer in Java, enabling a more functional programming style and reducing boilerplate code. They allow for cleaner, more concise, and readable code, especially when working with collections and streams.