Explain functional interfaces.
Functional interfaces are a key feature introduced in Java 8 that enable the use of lambda expressions. They are interfaces that contain exactly one abstract method, acting as a contract for lambda expressions and method references.
What are Functional Interfaces?
In Java, a functional interface is an interface that has exactly one abstract method. This single abstract method is known as the Functional Method or Single Abstract Method (SAM). Functional interfaces are designed to be targets for lambda expressions and method references, making code more concise and readable by allowing behavior to be passed as an argument.
Key Characteristics
- Single Abstract Method (SAM): Must declare exactly one abstract method. Methods inherited from
java.lang.Object(likeequals,hashCode,toString) do not count towards this single abstract method rule. - Default and Static Methods: Can include any number of default and static methods, which were also introduced in Java 8.
@FunctionalInterfaceAnnotation: This annotation is optional but highly recommended. It serves as an indicator for the compiler to enforce the single abstract method rule. If an interface annotated with@FunctionalInterfacehas more or less than one abstract method (excluding Object class methods), the compiler will flag an error.- Target for Lambda Expressions: They provide the context or target type required for lambda expressions and method references to work.
Syntax and Example
Defining a functional interface is similar to defining a regular interface, with the addition of the @FunctionalInterface annotation and ensuring only one abstract method. Lambda expressions then provide a concise way to implement this single abstract method.
@FunctionalInterface
interface MyFunctionalInterface {
void performAction(String data); // Single abstract method
// Optional: default method
default void logAction(String message) {
System.out.println("Logging: " + message);
}
// Optional: static method
static void displayInfo() {
System.out.println("This is a custom functional interface.");
}
}
public class FunctionalInterfaceDemo {
public static void main(String[] args) {
// Using a lambda expression to implement the functional interface
MyFunctionalInterface myAction = (data) -> {
System.out.println("Action performed with data: " + data.toUpperCase());
};
myAction.performAction("hello world"); // Output: Action performed with data: HELLO WORLD
myAction.logAction("Action completed."); // Output: Logging: Action completed.
MyFunctionalInterface.displayInfo(); // Output: This is a custom functional interface.
// Another way using method reference if a compatible method exists
// MyFunctionalInterface anotherAction = System.out::println;
// anotherAction.performAction("method reference example");
}
}
Pre-defined Functional Interfaces
Java provides a rich set of pre-defined functional interfaces in the java.util.function package, eliminating the need to create custom interfaces for common use cases. These include interfaces for functions, consumers, predicates, and suppliers.
| Interface | Abstract Method | Description |
|---|---|---|
| `Predicate<T>` | `boolean test(T t)` | Represents a predicate (boolean-valued function) of one argument. |
| `Consumer<T>` | `void accept(T t)` | Represents an operation that accepts a single input argument and returns no result. |
| `Function<T, R>` | `R apply(T t)` | Represents a function that accepts one argument and produces a result. |
| `Supplier<T>` | `T get()` | Represents a supplier of results. Has no arguments. |
| `UnaryOperator<T>` | `T apply(T t)` | Represents an operation on a single operand that produces a result of the same type as its operand. Extends `Function<T, T>`. |
| `BinaryOperator<T>` | `T apply(T t1, T t2)` | Represents an operation upon two operands of the same type, producing a result of the same type as the operands. Extends `BiFunction<T, T, T>`. |
| `BiPredicate<T, U>` | `boolean test(T t, U u)` | Represents a predicate (boolean-valued function) of two arguments. |
| `BiConsumer<T, U>` | `void accept(T t, U u)` | Represents an operation that accepts two input arguments and returns no result. |
| `BiFunction<T, U, R>` | `R apply(T t, U u)` | Represents a function that accepts two arguments and produces a result. |
Why Use Functional Interfaces?
- Enables Lambda Expressions: They are the fundamental building blocks for using lambda expressions, allowing behavior to be passed around as an argument.
- Concise Code: Reduces boilerplate code significantly compared to traditional anonymous inner classes.
- Readability: Makes code cleaner and easier to understand, especially for simple operations.
- Integration with Streams API: Functional interfaces are heavily used in Java's Streams API to perform operations like filtering, mapping, and reducing data collections in a declarative style.
- Supports Functional Programming: Facilitates a more functional programming style in Java, promoting immutability and side-effect-free operations.