Java Functional Interface
In this article, we’ll go through what Functional Interface is in Java and how you can use them.
1. What is a Functional Interface in Java?
Functional Interfaces were introduced in Java 8 and these Interfaces are annotated with@FunctionalInterface
annotation. By definition, a Functional Interface is an Interface that has exactly one abstract method. This means that it can have as many static
and default
methods as possible and still be considered a Functional Interface.
Furthermore, you can implement a Functional Interface by using a Lambda Expression, a Method reference, or a Constructor reference. Let’s go through the valid syntaxes:
1.1 One-Line Lambda
//no parameters
syntax () -> void_or_return_something
// one parameter
a_param -> void_or_return_something
// two or more parameters
(param_1, param2, param_3, ... , param_n) -> void_or_return_something
1.2 Method and Constructor Reference
// zero, one or more parameters
A_class_name_or_object::void_or_with_return_type_method
// constructor reference A_class_name::new
A method reference can replace on specific occasions the lambda expression syntax.
1.3 Multiline Lambda
//no parameters syntax
() -> {
//lines_of_code void_or_return_something;
};
// one parameter
a_param -> {
//lines_of_code void_or_return_something;
};
// two or more parameters
(param_1, param2, param_3, ... , param_n) -> {
//lines_of_code void_or_return_something;
};
2. Functional Interfaces Introduced in Java 8
In this section, we’ll go through the most common Functional Interfaces, all of which are under java.util.function
package.
2.1 Consumer Functional Interface
Consumer
just accepts one parameter and returns nothing. It has two methods:
The second method allows you to chain consumers. Let’s see a common use case for Consumer
:
Stream.of("Code", "Learn", "Hub")
//.forEach(System.out::println); this is exactly the same
.forEach(s -> System.out.println(s));
Consumer<String> printer = System.out::println;
Consumer<String> printAgainWithStars = s -> System.out.println(s+"*");
Stream.of("C", "L", "H")
.forEach(printer.andThen(printAgainWithStars));
First, we implemented the Consumer Functional interface inside the forEach()
. Then, we defined two Consumers, one which just prints every string, and another that prints each string with a star at the end. Finally, we passed the printer Consumer
and chained it with the second printer. The above snippet will print the following:
Code
Learn
Hub
C
C*
L
L*
H
H*
2.2 Function Functional Interface
Function
just accepts a parameter of type T and returns a value of type R. It has 4 methods:
R apply(T t)
: This method just applies the function given a value and returns a value R.default Function compose(Function before)
: It applies thebefore Function
before the function calledcompose
is executed.static Function identity()
: This is a function that returns whatever it was given as a parameter.default Function andThen(Function after)
: It applies theafter Function
after the function that calledandThen
is executed.
The most common use case of Function is when using the map method of the Stream class.
2.3 Supplier Functional Interface
Supplier
just accepts no parameter and returns a value. It has only one method T get()
which returns the result.
2.4 Predicate Functional Interface
Predicate
accepts a parameter and returns a Boolean
value. As you can infer from the name, It is mainly used to filter elements of a stream.
Predicate
has 6 methods:
boolean test(T t)
: This method returns the result.default Predicate and(Predicate other)
: It allows you to chain Predicates using the AND operator and returns aPredicate
.default Predicate negate()
: It negates thePredicate
.default Predicate or(Predicate other)
: You can chain Predicates using the OR operator and returns aPredicate
.static Predicate isEqual(Object targetRef)
: You can directly check if the element is equal to another element.static Predicate not(Predicate target)
: It allows you to chain Predicates using the NOT operator and returns aPredicate
.
Let’s see Predicate
in action:
Stream.of("Hello", "World!", "Code!", "LEARN!", "hub").
filter(
Predicate.isEqual("Hello")
.or(
Predicate.not(s -> ((String) s).contains("!"))
)
.negate()
)
.forEach(System.out::println);
Here the condition we formed is the following equivalent to pre-Java 8 Logical Operators:
!(s.equals("hello") || !s.contains("!"))
Of course, the output is the following:
2.5 Operator Suffix Functional Interfaces
All these Functional Interfaces (e.g. BinaryOperator
, UnaryOperator
, etc.) have only one difference with Function
Functional Interface that we mentioned before; they only have one type that both parameters and return type must match.
3. Creating Your Own Functional Interfaces
So now you know which Functional Interfaces exist but what if you need a Function
that accepts three parameters? Of course, you can create your own custom functional interfaces; below you will find a TriFunction
Functional Interface:
@FunctionalInterface
public interface TriFunction<T, U, Q, R>{
R apply(T t, U u , Q q);
}
You can create a TriPredicate
, TriConsumer
, TriSupplier
, or TripleOperator
as well.
Now we will create a Record
as shown below:
private record Person(String name, String surname, Integer age){};
Then, let’s say we need to concatenate all of a Person’s attributes, therefore, we should implement the TriFunction
Functional Interface and use it to accommodate our needs:
TriFunction<String, String, Integer, String> function = (s1, s2, i1) -> s1 + s2 + i1;
Stream.of(
new Person("Geo", "Pal", 26),
new Person("Dim", "Tas", 35),
new Person("Ion", "Mak", 30)
).map(p -> function.apply(p.name(), p.surname(), p.age()))
.forEach(System.out::println);
As you can observe, there isn’t anything special about implementing TriFunction
; we just need to accept 3 parameters and return the result. Thus, we are now able to use this function inside map method of stream and get the required result:
GeoPal26
DimTas35
IonMak30
4. Conclusion
To sum up, you should be able to use any of the Functional Interfaces under java.util.function
package and create your own Functional Interfaces. You can find the source code on our GitHub page.