Java : Functional Interface
In this article, We will explore the fundamental concepts around Functional Interfaces, understand their purpose, characteristics, and how they relate to Lambda Functions and Method References. We will also explore various built-in functional interfaces provided by Java in the package java.util.function and learn how to create custom functional interfaces through practical examples.
What are Functional Interface?
A Functional Interface is an Java interface that contains only a Single Abstract Method (Hence, also known as SAM Interface). Since functional interfaces have only one abstract method they server as the target type for lambda functions and method references.
Functional interfaces can have multiple
defaultandstaticmethods, But they must strictly have only one abstract method.
To indicate that an interface is intended to be a functional interface, we use the @FunctionalInterfaceannotation. Using the annotation is optional, but it is a good practice as it helps the developers and IDE to understand the intended use of the interface.
When an interface is marked with
@FunctionalInterfaceannotation and it has more than one abstract method, the compiler flags an “Unexpected@FunctionalInterfaceannotation” error.
Build-In Functional Interface
Java provides a set of built-in functional interfaces under the package java.util.function to cover various use cases. They can be categorized into four core types. Consumer, Supplier, Predicate and Function.
1. Consumer
The Consumer functional interface represents an operation that takes a single input of type T and does not return any value.
@FunctionalInterface
interface Consumer<T> {
void accept(T value);
}The following example shows the usage of Consumer interface, for printing the elements of a List.
import java.util.List;
import java.util.function.Consumer;
public Example {
public static void main(String[] args) {
List<String> names = List.of("Tony", "Peter", "Bruce");
Consumer<String> print = name -> System.out.println(name);
names.forEach(print);
}
}2. Supplier
The Supplier functional interface represents an operation that does not take any input and returns a value of type T.
@FunctionalInterface
interface Supplier<T> {
T get();
}The following example shows the usage of Supplier interface, for generating random number.
import java.util.function.Supplier;
public Example {
public static void main(String[] args) {
Supplier<Double> randomNumberGenerator = () -> Math.random();
System.out.println(randomNumberGenerator.get());
}
}3. Predicate
The Predicate functional interface represents an operation that takes a single input of type T and returns a boolean value.
@FunctionalInterface
interface Predicate<T> {
boolean test(T value);
}The following example shows the usage of Predicate interface, to extract even numbers from a List.
import java.util.function.Predicate;
public Example {
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Predicate<Integer> check = i -> i % 2 == 0;
List<Integer> evenNumbers = numbers.stream()
.filter(check)
.collect(Collectors.toList());
}
}Get Guhan B’s stories in your inbox
Join Medium for free to get updates from this writer.
4. Function
The Function functional interface represents an operation that takes a single input of type T and returns a value of type R.
@FunctionalInterface
interface Function<T, R> {
R apply(T value);
}The following example shows the usage of Function interface, to square the numbers on a List.
import java.util.function.Function;
public Example {
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Function<Integer, Integer> square = i -> i * i;
List<Integer> squaredNumbers = numbers.stream()
.map(square)
.collect(Collectors.toList());
}
}Variants of Consumer, Supplier, Predicate & Function
Apart from the four core functional interfaces describe above, Java also provides multiple specialized variants of these four core built-in interfaces based on the number or type of input and type of output. The exhaustive list of all the functional interfaces provided by Java can be found in the official Java API documentation here.
Primitive based functional interfaces like
IntConsumer,IntSupplier,IntPredicate,IntFunctionetc. are provided to avoid the overhead of autoboxing and unboxing while using wrapper classes.
Custom Functional Interface
The built-in functional interfaces provided by Java will be useful for a wide range of scenarios, but you may encounter scenarios where none of the built-in functional interfaces meets your requirements. In such cases, you can create your own custom function interfaces.
- Define a Java interface that has a single abstract method.
- Optionally mark it with the
@FunctionalInterfaceannotation.
@FunctionalInterface
interface CustomFunctionalInterface<T> {
void action(T value);
}Now the CustomFunctionalInterface can be used with a suitable lambda function or a method reference.
Functional Interfaces and Method References
Method Reference is a feature in Java using which normal methods can be passed instead of lambda functions to functional interfaces. Consider the example of Consumer functional interface, where we create a lambda method which calls System.out.println. Using method reference we can directly pass the System.out.println.
import java.util.List;
import java.util.function.Consumer;
public Example {
public static void main(String[] args) {
List<String> names = List.of("Tony", "Peter", "Bruce");
names.forEach(System.out::println);
}
}Method reference uses the :: operator to separate the object / class from the method. There are four variants of method references
- Reference to Static Methods
class::staticMethod - Reference to Instance Method (Using instance)
object::instanceMethod - Reference to Instance Method (Using class)
class::instanceMethod - Reference to Constructor Method
class::neworclass[]::new
Method references can also be use with
thisandsuperkeywords.
Conclusion
In this article, we explored Functional Interfaces in Java — interfaces with a single abstract method that serve as the foundation for lambda functions and method references. We examined their characteristics, the role of the @FunctionalInterface annotation, and how they enable functional programming in Java. We also walked through the core built-in interfaces like Consumer, Supplier, Predicate, and Function, along with practical examples and Method References. As the backbone of lambda functions, understanding functional interfaces is essential for unlocking the full potential of modern Java.









