DEV Community

Abhishek Singh
Abhishek Singh

Posted on

Java 8 Lambda Expressions and Functional Interfaces

Overview :

Java 8 introduced Lambda Expressions and Functional Interfaces, which bring functional programming capabilities to Java. These features allow for more concise and readable code, especially when working with collections and performing common tasks like filtering, mapping, and reducing

By the end of this blog, we will understand:

What Lambda Expressions are?
What Functional Interfaces are?
How to create and use Lambda Expressions?
Common use cases for Lambda Expressions and Functional Interfaces

What are Lambda Expressions?
Lambda Expressions are a way to provide clear and concise syntax for writing anonymous methods (functions). They enable you to treat functionality as a method argument, or pass a block of code around as data

Syntax of Lambda Expressions :
The basic syntax of a lambda expression is:

(parameters) -> expression
or
(parameters) -> { statements; }
Enter fullscreen mode Exit fullscreen mode

Example: Simple Lambda Expression

// Traditional way using an anonymous class
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }
};

// Using a lambda expression
Runnable lambdaRunnable = () -> System.out.println("Hello, world!");

Enter fullscreen mode Exit fullscreen mode

What are Functional Interfaces?
A Functional Interface is an interface with a single abstract method. They can have multiple default or static methods but only one abstract method. Lambda expressions can be used to instantiate functional interfaces.

Example: Defining a Functional Interface

@FunctionalInterface
public interface MyFunctionalInterface {
    void myMethod();
}

Enter fullscreen mode Exit fullscreen mode

Java 8 includes several built-in functional interfaces in the java.util.function package, such as Predicate, Function, Consumer, and Supplier.

Creating and Using Lambda Expressions :

Example 1: Using Predicate Interface
The Predicate interface represents a boolean-valued function of one argument.

import java.util.function.Predicate;

public class Main {
    public static void main(String[] args) {
        Predicate<String> isEmpty = (str) -> str.isEmpty();

        System.out.println(isEmpty.test("")); // Output: true
        System.out.println(isEmpty.test("Hello")); // Output: false
    }
}

Enter fullscreen mode Exit fullscreen mode

Example 2: Using Function Interface
The Function interface represents a function that takes one argument and produces a result.

import java.util.function.Function;

public class Main {
    public static void main(String[] args) {
        Function<Integer, String> intToString = (num) -> "Number: " + num;

        System.out.println(intToString.apply(5)); // Output: Number: 5
    }
}

Enter fullscreen mode Exit fullscreen mode

Example 3: Using Consumer Interface
The Consumer interface represents an operation that takes a single argument and returns no result.

import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        Consumer<String> printUpperCase = (str) -> System.out.println(str.toUpperCase());

        printUpperCase.accept("hello"); // Output: HELLO
    }
}

Enter fullscreen mode Exit fullscreen mode

Example 4: Using Supplier Interface
The Supplier interface represents a supplier of results, which doesn't take any arguments and returns a result.
import java.util.function.Supplier;

public class Main {
    public static void main(String[] args) {
        Supplier<String> helloSupplier = () -> "Hello, world!";

        System.out.println(helloSupplier.get()); // Output: Hello, world!
    }
}
Enter fullscreen mode Exit fullscreen mode

Common Use Cases for Lambda Expressions and Functional Interfaces :

Use Case 1: Filtering Collections

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

        // Filter names that start with 'A'
        List<String> filteredNames = names.stream()
          .filter(name -> name.startsWith("A"))
              .collect(Collectors.toList());

        filteredNames.forEach(System.out::println);             // Output: Alice
    }
}

Enter fullscreen mode Exit fullscreen mode

Use Case 2: Mapping Collections

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

        // Convert names to uppercase
        List<String> upperCaseNames = names.stream()
                                           .map(String::toUpperCase)
                                           .collect(Collectors.toList());

        upperCaseNames.forEach(System.out::println); // Output: ALICE, BOB, CHARLIE, DAVID
    }
}

Enter fullscreen mode Exit fullscreen mode

Use Case 3: Reducing Collections

import java.util.Arrays;
import java.util.List;

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

        // Sum of all numbers
        int sum = numbers.stream()
                         .reduce(0, Integer::sum);

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

Enter fullscreen mode Exit fullscreen mode

Use Case 4: Creating Custom Functional Interfaces

@FunctionalInterface
interface MathOperation {
    int operate(int a, int b);
}

public class Main {
    public static void main(String[] args) {
        // Define lambda expressions for addition and multiplication
        MathOperation addition = (a, b) -> a + b;
        MathOperation multiplication = (a, b) -> a * b;

        System.out.println("Addition: " + addition.operate(5, 3)); // Output: Addition: 8
        System.out.println("Multiplication: " + multiplication.operate(5, 3)); // Output: Multiplication: 15
    }
}

Enter fullscreen mode Exit fullscreen mode

Summary
Lambda Expressions and Functional Interfaces are powerful features in Java 8 that enable you to write more concise, readable, and functional-style code. They are particularly useful for operations on collections and data, allowing you to:

Filter: Select elements based on a condition.
Map: Transform elements.
Reduce: Combine elements into a single result.
Custom Functional Interfaces: Define your own interfaces for specific tasks

Happy Coding...

Top comments (0)