Java Functional Interfaces
A functional interface is an interface that contains exactly one abstract method. Functional interfaces are the foundation of lambda expressions and functional programming in Java.
Java provides several built-in functional interfaces in the java.util.function package, making it easy to pass behavior as a method argument.
Creating a Functional Interface
The @FunctionalInterface annotation tells the compiler that an interface is intended to contain one abstract method.
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}
public class Main {
public static void main(String[] args) {
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;
System.out.println(add.calculate(10, 5));
System.out.println(multiply.calculate(10, 5));
}
}
Predicate
Predicate
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
Predicate<Integer> positive = number -> number > 0;
System.out.println(positive.test(10));
System.out.println(positive.test(-5));
}
}
Consumer
Consumer
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
Consumer<String> printer = text -> System.out.println(text);
printer.accept("Hello Java");
}
}
Supplier
Supplier
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) {
Supplier<String> message = () -> "Hello from Supplier";
System.out.println(message.get());
}
}
Function
Function
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
Function<String, Integer> length = text -> text.length();
System.out.println(length.apply("Java"));
}
}
BiFunction
BiFunction accepts two arguments and returns a result.
import java.util.function.BiFunction;
public class Main {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
System.out.println(add.apply(10, 20));
}
}
UnaryOperator
UnaryOperator
import java.util.function.UnaryOperator;
public class Main {
public static void main(String[] args) {
UnaryOperator<Integer> doubleValue = number -> number * 2;
System.out.println(doubleValue.apply(10));
}
}
BinaryOperator
BinaryOperator
import java.util.function.BinaryOperator;
public class Main {
public static void main(String[] args) {
BinaryOperator<Integer> max = Integer::max;
System.out.println(max.apply(10, 25));
}
}
Composing Functions
Function interfaces provide methods such as andThen and compose, allowing multiple transformations to be combined.
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
Function<String, String> trim = String::trim;
Function<String, String> upper = String::toUpperCase;
Function<String, String> process = trim.andThen(upper);
System.out.println(process.apply(" hello java "));
}
}
When to Create a Custom Functional Interface
Standard interfaces should be preferred when they clearly describe the required behavior. Create a custom functional interface when the operation has domain-specific meaning or needs a clearer API.
@FunctionalInterface
interface PriceCalculator {
double calculate(double price);
}
public class Main {
public static void main(String[] args) {
PriceCalculator discount = price -> price * 0.90;
System.out.println(discount.calculate(1000));
}
}
Common Functional Interfaces
Predicate<T> : T -> boolean
Consumer<T> : T -> void
Supplier<T> : () -> T
Function<T, R> : T -> R
BiFunction<T,U,R> : (T,U) -> R
UnaryOperator<T> : T -> T
BinaryOperator<T> : (T,T) -> T
Best Practices
Use standard functional interfaces when they accurately represent the operation. Keep lambda expressions small and readable, and use method references when they make the code clearer.
Use @FunctionalInterface for custom functional interfaces so the compiler can verify that the interface contains only one abstract method.
Conclusion
Functional interfaces are a core part of modern Java. Predicate, Consumer, Supplier, Function, and their specialized variants allow behavior to be passed around as values and work naturally with lambda expressions and streams.