A Functional Interface is an interface with a single abstract method but can have multiple default or static methods. They are the backbone of Lambda Expressions and Stream API in Java.
Example of Functional Interface
@FunctionalInterface
interface Greeting {
void sayHello(String name);
}
Built-in Functional Interfaces
Java provides several built-in functional interfaces in the java.util.function
package:
- Predicate<T> — Returns a boolean value
- Consumer<T> — Takes an argument but returns nothing
- Function<T, R> — Takes an argument and returns a result
- Supplier<T> — Supplies a result without taking any arguments
- UnaryOperator<T> — A special type of Function for same input and output type
- BinaryOperator<T> — A special type of BiFunction for same input and output type
- BiPredicate<T, U>, BiConsumer<T, U>, BiFunction<T, U, R> — Variants for two arguments
Predicate<T> Examples
Used for conditional checks.
import java.util.function.Predicate;
import java.util.List;
import java.util.Arrays;
public class PredicateExample {
public static void main(String[] args) {
Predicate<Integer> isEven = x -> x % 2 == 0;
System.out.println(isEven.test(4)); // Output: true
// Example with Collection
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
numbers.stream()
.filter(isEven)
.forEach(System.out::println); // Output: 2 4 6
// Combining Predicates
Predicate<Integer> isPositive = x -> x > 0;
numbers.stream()
.filter(isEven.and(isPositive))
.forEach(System.out::println); // Output: 2 4 6
}
}
Consumer<T> Examples
Accepts a single input but does not return any result.
import java.util.function.Consumer;
import java.util.List;
import java.util.Arrays;
public class ConsumerExample {
public static void main(String[] args) {
Consumer<String> print = s -> System.out.println(s);
print.accept("Hello, Consumer!");
// Example with Collection
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(print); // Output: Alice Bob Charlie
}
}
Function<T, R> Examples
Takes an input and returns a result.
import java.util.function.Function;
import java.util.List;
import java.util.Arrays;
public class FunctionExample {
public static void main(String[] args) {
Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(5)); // Output: 25
// Using Function in Stream
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
.map(square)
.forEach(System.out::println); // Output: 1 4 9 16 25
// Composing Functions
Function<Integer, Integer> doubleIt = x -> x * 2;
Function<Integer, Integer> squareThenDouble = square.andThen(doubleIt);
System.out.println(squareThenDouble.apply(3)); // Output: 18
}
}
Supplier<T> Examples
Does not take any input but 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());
}
}
UnaryOperator<T> and BinaryOperator<T> Examples
Special forms of Function and BiFunction for operations on the same type.
import java.util.function.UnaryOperator;
import java.util.function.BinaryOperator;
public class OperatorExample {
public static void main(String[] args) {
// UnaryOperator
UnaryOperator<Integer> square = x -> x * x;
System.out.println(square.apply(5)); // Output: 25
// BinaryOperator
BinaryOperator<Integer> multiply = (a, b) -> a * b;
System.out.println(multiply.apply(2, 3)); // Output: 6
}
}
Custom Functional Interface Example
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
public class CustomFunctionalInterfaceExample {
public static void main(String[] args) {
MathOperation addition = (a, b) -> a + b;
MathOperation multiplication = (a, b) -> a * b;
System.out.println(addition.operate(5, 3)); // Output: 8
System.out.println(multiplication.operate(5, 3)); // Output: 15
}
}