Java Functional Interface

From bibbleWiki
Jump to navigation Jump to search

Java Functional Interface

A **Functional Interface** in Java is an interface that contains exactly one abstract method. It can have multiple default or static methods, but only one abstract method is allowed. Functional interfaces are the foundation of **lambda expressions** and **method references** introduced in **Java 8**.

Key Characteristics

  • Contains exactly one abstract method.
  • Can have default and static methods.
  • Annotated with @FunctionalInterface (optional but recommended for clarity).
  • Enables functional programming in Java.

Examples of Built-in Functional Interfaces

Java provides several built-in functional interfaces in the java.util.function package:

Functional Interface Abstract Method Description Example
Function<T, R> R apply(T t) Takes one argument and produces a result. String::length
Consumer<T> void accept(T t) Takes one argument and performs an operation without returning a result. System.out::println
Supplier<T> T get() Produces a result without taking any arguments. () -> "Hello"
Predicate<T> boolean test(T t) Evaluates a condition and returns a boolean. x -> x > 10
BiFunction<T, U, R> R apply(T t, U u) Takes two arguments and produces a result. (x, y) -> x + y

Before and After Functional Interfaces

Before Java 8 (Using Anonymous Classes)

import java.util.ArrayList;
import java.util.List;

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

        // Using an anonymous class to filter names
        List<String> filteredNames = filterNames(names, new Predicate<String>() {
            @Override
            public boolean test(String name) {
                return name.startsWith("A");
            }
        });

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

    public static List<String> filterNames(List<String> names, Predicate<String> predicate) {
        List<String> result = new ArrayList<>();
        for (String name : names) {
            if (predicate.test(name)) {
                result.add(name);
            }
        }
        return result;
    }
}

After Java 8 (Using Lambda Expressions)

import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

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

        // Using a lambda expression to filter names
        List<String> filteredNames = names.stream()
                                          .filter(name -> name.startsWith("A"))
                                          .collect(Collectors.toList());

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

Custom Functional Interface

You can create your own functional interface by defining an interface with a single abstract method.

@FunctionalInterface
public interface StringProcessor {
    String process(String input);
}

Example Usage

public class CustomFunctionalInterfaceExample {
    public static void main(String[] args) {
        // Using a lambda expression
        StringProcessor toUpperCase = input -> input.toUpperCase();
        System.out.println(toUpperCase.process("hello")); // Output: HELLO

        // Using a method reference
        StringProcessor trim = String::trim;
        System.out.println(trim.process("  hello  ")); // Output: hello
    }
}

Advanced Features

Chaining Functional Interfaces

You can chain functional interfaces using methods like andThen and compose.

import java.util.function.Function;

public class FunctionChainingExample {
    public static void main(String[] args) {
        Function<String, Integer> stringLength = String::length;
        Function<Integer, Integer> square = x -> x * x;

        // Chain functions
        Function<String, Integer> lengthThenSquare = stringLength.andThen(square);
        System.out.println(lengthThenSquare.apply("Hello")); // Output: 25
    }
}

Combining Predicates

You can combine Predicate instances using and, or, and negate.

import java.util.function.Predicate;

public class PredicateExample {
    public static void main(String[] args) {
        Predicate<String> startsWithA = s -> s.startsWith("A");
        Predicate<String> endsWithZ = s -> s.endsWith("Z");

        Predicate<String> combined = startsWithA.and(endsWithZ);

        System.out.println(combined.test("Alice")); // Output: false
        System.out.println(combined.test("AtoZ")); // Output: true
    }
}

Benefits of Functional Interfaces

  • **Conciseness**: Reduces boilerplate code by replacing anonymous classes with lambda expressions.
  • **Reusability**: Enables passing behavior as parameters, making code more modular.
  • **Stream API Integration**: Works seamlessly with the Stream API for declarative programming.
  • **Improved Readability**: Simplifies code, making it easier to understand.

Summary

Functional interfaces are a cornerstone of functional programming in Java. They enable concise, reusable, and readable code by allowing behavior to be passed as parameters. By leveraging built-in functional interfaces and creating custom ones, you can write modern, maintainable Java applications.