A functional interface in Java is an interface that contains exactly one abstract method. They can have multiple default or static methods, but only one abstract method. This allows functional interfaces to be used as the assignment target for lambda expressions and method references.
The introduction of functional interfaces in Java 8 was a significant enhancement, paving the way for functional programming capabilities within the language.
Example of a Functional Interface:
@FunctionalInterface
interface MyFunctionalInterface {
void execute(); // Single abstract method
}
Java provides several built-in functional interfaces in the java.util.function
package, which can be categorized as follows:
Predicate<T>: Represents a boolean-valued function of one argument.
boolean test(T t)
Function<T, R>: Represents a function that accepts one argument and produces a result.
R apply(T t)
Consumer<T>: Represents an operation that accepts a single input argument and returns no result.
void accept(T t)
Supplier<T>: Represents a supplier of results.
T get()
UnaryOperator<T>: Represents a function that accepts a single argument and returns a result of the same type.
T apply(T t)
BinaryOperator<T>: Represents a function that takes two arguments of the same type and returns a result of the same type.
T apply(T t1, T t2)
You can create your own functional interfaces as needed. Here’s a more detailed example of how to define and use a custom functional interface.
@FunctionalInterface
interface StringManipulator {
String manipulate(String input);
}
In this example, StringManipulator
is a functional interface with a single abstract method manipulate
.
Functional interfaces are often used in conjunction with lambda expressions and method references. Let’s explore how to implement and use them.
public class LambdaExample {
public static void main(String[] args) {
// Implementing StringManipulator using a lambda expression
StringManipulator toUpperCase = input -> input.toUpperCase();
String result = toUpperCase.manipulate("hello");
System.out.println(result); // Outputs: HELLO
}
}
In this example, we implemented the StringManipulator
interface with a lambda expression that converts a string to uppercase.
Let’s utilize the built-in functional interfaces provided by the Java standard library.
import java.util.function.Predicate;
import java.util.function.Function;
public class FunctionalInterfaceExample {
public static void main(String[] args) {
// Using Predicate
Predicate isNotEmpty = str -> !str.isEmpty();
System.out.println(isNotEmpty.test("")); // Outputs: false
System.out.println(isNotEmpty.test("test")); // Outputs: true
// Using Function
Function stringLength = str -> str.length();
System.out.println(stringLength.apply("Hello")); // Outputs: 5
}
}
In this example, we use Predicate
to check if a string is not empty and Function
to calculate the length of a string.
Functional interfaces are widely used in Java, especially when working with collections and streams. Here are a few practical applications:
Functional interfaces make it easier to perform operations like filtering, mapping, and reducing collections.
Example: Using Streams with Functional Interfaces
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
List numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// Filtering even numbers using Predicate
List evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("Even numbers: " + evenNumbers); // Outputs: [2, 4, 6]
}
}
In this example, we used a lambda expression to filter even numbers from a list using the filter
method, which takes a Predicate
as an argument.
Lambda expressions can also be used for sorting collections by providing a custom comparator.
Example: Sorting a List of Strings
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SortExample {
public static void main(String[] args) {
List names = new ArrayList<>();
names.add("John");
names.add("Alice");
names.add("Bob");
// Sorting using a lambda expression
Collections.sort(names, (a, b) -> a.compareTo(b));
names.forEach(name -> System.out.println(name)); // Outputs: Alice, Bob, John
}
}
Here, we sort a list of names using a lambda expression that implements the Comparator
interface.
Method references provide a way to refer to methods without executing them. They are often used as shorthand for lambda expressions when the lambda expression simply calls a method.
Example of Method Reference
import java.util.Arrays;
import java.util.List;
public class MethodReferenceExample {
public static void main(String[] args) {
List names = Arrays.asList("John", "Alice", "Bob");
// Using method reference to print names
names.forEach(System.out::println); // Outputs: John, Alice, Bob
}
}
In this example, we use a method reference to print each name from the list.