Java Functional Interface
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.