Functional Interfaces in Java
A functional interface is an interface that contains exactly one abstract method.
It may also contain multiple default or static methods, but as long as it has only one abstract method, it qualifies as a functional interface.
Functional interfaces enable the use of lambda expressions, allowing for the concise representation of single-method interfaces.
Why Functional Interfaces?
Lambda Expressions:
- Functional interfaces are a prerequisite for using lambda expressions. Lambda expressions provide a concise way to express instances of single-method interfaces without the need for verbose anonymous class implementations.
Method References:
- Functional interfaces are crucial for method references, another feature introduced in Java 8. Method references allow the reference to a method by its name, providing a shorthand notation for lambda expressions.
Stream API:
- The Stream API, introduced in Java 8, heavily relies on functional interfaces. Operations like
filter
,map
, andreduce
expect functional interfaces as parameters, enabling a more functional and declarative style of coding.
- The Stream API, introduced in Java 8, heavily relies on functional interfaces. Operations like
Creating a Functional Interface:
Let's create a simple functional interface named Calculator
:
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
// Uncommenting the line below will result in a compilation error
// int multiply(int a, int b);
}
In this example, Calculator
is annotated with @FunctionalInterface
to indicate that it should be treated as a functional interface. It declares a single abstract method calculate
that takes two integers and returns an integer.
Using Functional Interfaces:
Let's use the Calculator
functional interface in a simple program:
public class FunctionalInterfaceExample {
public static void main(String[] args) {
// Using lambda expression
Calculator addition = (a, b) -> a + b;
System.out.println("Sum: " + addition.calculate(5, 3));
// Using a method reference
Calculator subtraction = FunctionalInterfaceExample::subtract;
System.out.println("Difference: " + subtraction.calculate(5, 3));
}
private static int subtract(int a, int b) {
return a - b;
}
}
Output:
Running the program will produce the following output:
Sum: 8
Difference: 2
Explanation:
Lambda Expression:
- The
addition
instance is created using a lambda expression, implementing thecalculate
method to perform addition.
- The
Method Reference:
- The
subtraction
instance is created using a method reference to thesubtract
method, showcasing another way to implement thecalculate
method.
- The
Common Functional Interfaces in Java
The java.util.function package contains many built-in functional interfaces, which are commonly used in Java:
Predicate<T>
: Represents a boolean-valued function of one argument.
Predicate<Integer> isPositive = (num) -> num > 0;
System.out.println(isPositive.test(5)); // true
Consumer<T>
: Represents an operation that accepts a single input argument and returns no result.
Consumer<String> printer = (s) -> System.out.println(s);
printer.accept("Hello, world!"); // Prints: Hello, world!
Function<T, R>
: Represents a function that accepts one argument and produces a result.
Function<String, Integer> lengthFunction = (s) -> s.length();
System.out.println(lengthFunction.apply("Hello")); // 5
Supplier<T>
: Represents a supplier of results.
Supplier<Double> randomValue = () -> Math.random();
System.out.println(randomValue.get());
Detailed Examples of Common Functional Interfaces
Let's explore each of these interfaces in more detail with examples:
Predicate<T>
A Predicate is a functional interface that represents a condition or property that an object must satisfy. It takes one argument and returns a boolean value.
import java.util.function.Predicate;
public class PredicateExample {
public static void main(String[] args) {
Predicate<Integer> isEven = (num) -> num % 2 == 0;
System.out.println(isEven.test(4)); // true
System.out.println(isEven.test(5)); // false
}
}
In this example, the Predicate checks whether a number is even.
Consumer<T>
A Consumer is a functional interface that performs an operation on a single input argument. It does not return any result.
import java.util.function.Consumer;
public class ConsumerExample {
public static void main(String[] args) {
Consumer<String> toUpperCase = (s) -> System.out.println(s.toUpperCase());
toUpperCase.accept("hello"); // Prints: HELLO
}
}
In this example, the Consumer converts a string to uppercase and prints it.
Function<T, R>
A Function is a functional interface that takes one argument and produces a result.
import java.util.function.Function;
public class FunctionExample {
public static void main(String[] args) {
Function<String, Integer> stringLength = (s) -> s.length();
System.out.println(stringLength.apply("hello")); // 5
}
}
In this example, the Function calculates the length of a string.
Supplier<T>
A Supplier is a functional interface that represents a supplier of results. It does not take any arguments and returns a result.
import java.util.function.Supplier;
public class SupplierExample {
public static void main(String[] args) {
Supplier<Double> randomValue = () -> Math.random();
System.out.println(randomValue.get());
}
}
In this example, the Supplier generates a random value.
Summary
Functional Interface: An interface with exactly one abstract method, which can be implemented using lambda expressions.
@FunctionalInterface
Annotation: Helps to ensure the interface remains a functional interface.Common Functional Interfaces: Includes Predicate, Consumer, Function, and Supplier, each serving different purposes in functional programming.
Understanding functional interfaces and their uses is crucial for mastering modern Java programming, especially when working with lambda expressions and the Stream API.