0% found this document useful (0 votes)
12 views

functional_interfaces_java

This document serves as a comprehensive guide to functional interfaces in Java 8, detailing their use cases and implementations within the standard library. It covers various types of functional interfaces, including Function, Supplier, Consumer, Predicate, and Operator, along with their specialized versions for primitive types. The article also highlights the significance of lambda expressions and provides practical examples for better understanding.

Uploaded by

outlook vs
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views

functional_interfaces_java

This document serves as a comprehensive guide to functional interfaces in Java 8, detailing their use cases and implementations within the standard library. It covers various types of functional interfaces, including Function, Supplier, Consumer, Predicate, and Operator, along with their specialized versions for primitive types. The article also highlights the significance of lambda expressions and provides practical examples for better understanding.

Uploaded by

outlook vs
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

Functional Interfaces in Java | Baeldung https://www.baeldung.

com/java-8-functional-interfaces

(/)

Functional Interfaces in Java

Last updated: January 16, 2024

Written by: baeldung (https://www.baeldung.com/author/baeldung)

Reviewed by: Grzegorz Piwowarek (https://www.baeldung.com/editor/


grzegorz-author)

Core Java (https://www.baeldung.com/category/java/core-java)

>= Java 8 (https://www.baeldung.com/tag/jdk8-and-later)

Java Interfaces (https://www.baeldung.com/tag/java-interfaces)

1 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

1. Introduction (/)

This tutorial is a guide to different functional interfaces present in Java 8, as


well as their general use cases, and usage in the standard JDK library.

Further reading:
Iterable to Stream in Java (/?
post_type=post&p=16559)
The article explains how to convert an Iterable to Stream and why the
Iterable interface doesn't support it directly.

Read more (/?post_type=post&p=16559) →

How to Use if/else Logic in Java Streams (/?


post_type=post&p=41919)
Learn how to apply if/else logic to Java 8 Streams.

Read more (/?post_type=post&p=41919) →

2. Lambdas in Java 8

Java 8 brought a powerful new syntactic improvement in the form of


lambda expressions. A lambda is an anonymous function that we can
handle as a first-class language citizen. For instance, we can pass it to or
return it from a method.
Before Java 8, we would usually create a class for every case where we
needed to encapsulate a single piece of functionality. This implied a lot of
unnecessary boilerplate code to define something that served as a primitive
function representation.
The article “Lambda Expressions and Functional Interfaces: Tips and Best

2 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

Practices” (/java-8-lambda-expressions-tips) describes in more detail the


(/)
functional interfaces and best practices of working with lambdas. This guide
focuses on some particular functional interfaces that are present in the
java.util.function package.

3. Functional Interfaces

It’s recommended that all functional interfaces have an informative


@FunctionalInterface annotation. This clearly communicates the purpose of
the interface, and also allows a compiler to generate an error if the
annotated interface does not satisfy the conditions.
Any interface with a SAM(Single Abstract Method) is a functional
interface, and its implementation may be treated as lambda expressions.
Note that Java 8’s default methods are not abstract and do not count; a
functional interface may still have multiple default methods. We can
observe this by looking at the Function’s documentation (https://
docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/
function/Function.html).

4. Functions

The most simple and general case of a lambda is a functional interface with
a method that receives one value and returns another. This function of a
single argument is represented by the Function interface, which is
parameterized by the types of its argument and a return value:

public interface Function<T, R> { … }

One of the usages of the Function type in the standard library is the
Map.computeIfAbsent method. This method returns a value from a map by
key, but calculates a value if a key is not already present in a map. To
calculate a value, it uses the passed Function implementation:

3 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

Map<String, Integer> nameMap =(/)


new HashMap<>();
Integer value = nameMap.computeIfAbsent("John", s -> s.length());

In this case, we will calculate a value by applying a function to a key, put


inside a map, and also returned from a method call. We may replace the
lambda with a method reference that matches passed and returned
value types.
Remember that an object we invoke the method on is, in fact, the implicit
first argument of a method. This allows us to cast an instance method length
reference to a Function interface:

Integer value = nameMap.computeIfAbsent("John", String::length);

The Function interface also has a default compose method that allows us to
combine several functions into one and execute them sequentially:

Function<Integer, String> intToString = Object::toString;


Function<String, String> quote = s -> "'" + s + "'";

Function<Integer, String> quoteIntToString =


quote.compose(intToString);

assertEquals("'5'", quoteIntToString.apply(5));

The quoteIntToString function is a combination of the quote function applied


to a result of the intToString function.

5. Primitive Function Specializations

Since a primitive type can’t be a generic type argument, there are versions
of the Function interface for the most used primitive types double, int, long,
and their combinations in argument and return types:
• IntFunction, LongFunction, DoubleFunction: arguments are of specified
type, return type is parameterized
• ToIntFunction, ToLongFunction, ToDoubleFunction: return type is of

4 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

specified type, arguments are parameterized


(/)
• DoubleToIntFunction, DoubleToLongFunction, IntToDoubleFunction,
IntToLongFunction, LongToIntFunction, LongToDoubleFunction: having
both argument and return type defined as primitive types, as specified
by their names
As an example, there is no out-of-the-box functional interface for a function
that takes a short and returns a byte, but nothing stops us from writing our
own:

@FunctionalInterface
public interface ShortToByteFunction {

byte applyAsByte(s
short s);

Now we can write a method that transforms an array of short to an array of


byte using a rule defined by a ShortToByteFunction:

public byte[] transformArray(s


short[] array, ShortToByteFunction
function) {
byte[] transformedArray = new byte[array.length];
for (i
int i = 0; i < array.length; i++) {
transformedArray[i] = function.applyAsByte(array[i]);
}
return transformedArray;
}

Here’s how we could use it to transform an array of shorts to an array of


bytes multiplied by 2:

short[] array = {(s


short) 1, (s
short) 2, (s
short) 3};
byte[] transformedArray = transformArray(array, s -> (b
byte) (s * 2));

byte[] expectedArray = {(b


byte) 2, (b
byte) 4, (b
byte) 6};
assertArrayEquals(expectedArray, transformedArray);

6. Two-Arity Function Specializations


5 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

To define lambdas with two arguments, we have to use additional interfaces


(/)
that contain “Bi” keyword in their names: BiFunction, ToDoubleBiFunction,
ToIntBiFunction, and ToLongBiFunction.
BiFunction has both arguments and a return type generified, while
ToDoubleBiFunction and others allow us to return a primitive value.
One of the typical examples of using this interface in the standard API is in
the Map.replaceAll method, which allows replacing all values in a map with
some computed value.
Let’s use a BiFunction implementation that receives a key and an old value
to calculate a new value for the salary and return it.

Map<String, Integer> salaries = new HashMap<>();


salaries.put("John", 40000);
salaries.put("Freddy", 30000);
salaries.put("Samuel", 50000);

salaries.replaceAll((name, oldValue) ->


name.equals("Freddy") ? oldValue : oldValue + 10000);

7. Suppliers

The Supplier functional interface is yet another Function specialization that


does not take any arguments. We typically use it for lazy generation of
values. For instance, let’s define a function that squares a double value. It
will not receive a value itself, but a Supplier of this value:

public double squareLazy(Supplier<Double> lazyValue) {


return Math.pow(lazyValue.get(), 2);
}

This allows us to lazily generate the argument for invocation of this function
using a Supplier implementation. This can be useful if the generation of the
argument takes a considerable amount of time. We’ll simulate that using
Guava’s sleepUninterruptibly method:

6 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

(/) -> {
Supplier<Double> lazyValue = ()
Uninterruptibles.sleepUninterruptibly(1000, TimeUnit.MILLISECONDS);
return 9d;
};

Double valueSquared = squareLazy(lazyValue);

Another use case for the Supplier is defining logic for sequence generation.
To demonstrate it, let’s use a static Stream.generate method to create a
Stream of Fibonacci numbers:

int[] fibs = {0, 1};


Stream<Integer> fibonacci = Stream.generate(() -> {
int result = fibs[1];
int fib3 = fibs[0] + fibs[1];
fibs[0] = fibs[1];
fibs[1] = fib3;
return result;
});

The function that we pass to the Stream.generate method implements the


Supplier functional interface. Notice that to be useful as a generator, the
Supplier usually needs some sort of external state. In this case, its state
comprises the last two Fibonacci sequence numbers.
To implement this state, we use an array instead of a couple of variables
because all external variables used inside the lambda have to be
effectively final.
Other specializations of the Supplier functional interface include
BooleanSupplier, DoubleSupplier, LongSupplier and IntSupplier, whose
return types are corresponding primitives.

8. Consumers

As opposed to the Supplier, the Consumer accepts a generified argument


and returns nothing. It is a function that is representing side effects.
For instance, let’s greet everybody in a list of names by printing the greeting

7 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

in the console. The lambda passed to the List.forEach method implements


(/)
the Consumer functional interface:

List<String> names = Arrays.asList("John", "Freddy", "Samuel");


names.forEach(name -> System.out.println("Hello, " + name));

There are also specialized versions of the Consumer — DoubleConsumer,


IntConsumer and LongConsumer — that receive primitive values as
arguments. More interesting is the BiConsumer interface. One of its use
cases is iterating through the entries of a map:

Map<String, Integer> ages = new HashMap<>();


ages.put("John", 25);
ages.put("Freddy", 24);
ages.put("Samuel", 30);

ages.forEach((name, age) -> System.out.println(name + " is " + age + "


years old"));

Another set of specialized BiConsumer versions is comprised of


ObjDoubleConsumer, ObjIntConsumer, and ObjLongConsumer, which
receive two arguments; one of the arguments is generified, and the other is
a primitive type.

9. Predicates

In mathematical logic, a predicate is a function that receives a value and


returns a boolean value.
The Predicate functional interface is a specialization of a Function that
receives a generified value and returns a boolean. A typical use case of the
Predicate lambda is to filter a collection of values:

8 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

(/)
List<String> names = Arrays.asList("Angela", "Aaron", "Bob", "Claire",
"David");

List<String> namesWithA = names.stream()


.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());

In the code above, we filter a list using the Stream API and keep only the
names that start with the letter “A”. The Predicate implementation
encapsulates the filtering logic.
As in all of the previous examples, there are IntPredicate, DoublePredicate
and LongPredicate versions of this function that receive primitive values.

10. Operators

Operator interfaces are special cases of a function that receive and return
the same value type. The UnaryOperator interface receives a single
argument. One of its use cases in the Collections API is to replace all values
in a list with some computed values of the same type:

List<String> names = Arrays.asList("bob", "josh", "megan");

names.replaceAll(name -> name.toUpperCase());

The List.replaceAll function returns void as it replaces the values in place. To


fit the purpose, the lambda used to transform the values of a list has to
return the same result type as it receives. This is why the UnaryOperator is
useful here.
Of course, instead of name -> name.toUpperCase(), we can simply use a
method reference:

names.replaceAll(String::toUpperCase);

One of the most interesting use cases of a BinaryOperator is a reduction


operation. Suppose we want to aggregate a collection of integers in a sum

9 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

of all values. With Stream API, we could do this using a collector, but a more
(/)
generic way to do it would be to use the reduce method:

List<Integer> values = Arrays.asList(3, 5, 8, 9, 12);

int sum = values.stream()


.reduce(0, (i1, i2) -> i1 + i2);

The reduce method receives an initial accumulator value and a


BinaryOperator function. The arguments of this function are a pair of values
of the same type; the function itself also contains a logic for joining them in
a single value of the same type. The passed function must be associative,
which means that the order of value aggregation does not matter, i.e. the
following condition should hold:

op.apply(a, op.apply(b, c)) == op.apply(op.apply(a, b), c)

The associative property of a BinaryOperator operator function allows us to


easily parallelize the reduction process.
Of course, there are also specializations of UnaryOperator and
BinaryOperator that can be used with primitive values, namely
DoubleUnaryOperator, IntUnaryOperator, LongUnaryOperator,
DoubleBinaryOperator, IntBinaryOperator, and LongBinaryOperator.

11. Legacy Functional Interfaces

Not all functional interfaces appeared in Java 8. Many interfaces from


previous versions of Java conform to the constraints of a
FunctionalInterface, and we can use them as lambdas. Prominent examples
include the Runnable and Callable interfaces that are used in concurrency
APIs. In Java 8, these interfaces are also marked with a @FunctionalInterface
annotation. This allows us to greatly simplify concurrency code:

Thread thread = new Thread(() -> System.out.println("Hello From Another


Thread"));
thread.start();

10 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

12. Conclusion (/)

In this article, we examined different functional interfaces present in the Java


8 API that we can use as lambda expressions.
The source code for the article is available over on GitHub (https://
github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-
lambdas).

COURSES

ALL COURSES (/COURSES/ALL-COURSES)


BAELDUNG ALL ACCESS (/COURSES/ALL-ACCESS)
BAELDUNG ALL TEAM ACCESS (/COURSES/ALL-ACCESS-TEAM)
THE COURSES PLATFORM (HTTPS://COURSES.BAELDUNG.COM)

SERIES

JAVA “BACK TO BASICS” TUTORIAL (/JAVA-TUTORIAL)


LEARN SPRING BOOT SERIES (/SPRING-BOOT)
SPRING TUTORIAL (/SPRING-TUTORIAL)
GET STARTED WITH JAVA (/GET-STARTED-WITH-JAVA-SERIES)
SECURITY WITH SPRING (/SECURITY-SPRING)
REST WITH SPRING SERIES (/REST-WITH-SPRING-SERIES)
ALL ABOUT STRING IN JAVA (/JAVA-STRING)

ABOUT

ABOUT BAELDUNG (/ABOUT)


THE FULL ARCHIVE (/FULL_ARCHIVE)
EDITORS (/EDITORS)

11 of 12 07/03/25, 4:00 pm
Functional Interfaces in Java | Baeldung https://www.baeldung.com/java-8-functional-interfaces

OUR PARTNERS (/PARTNERS/)


(/)
PARTNER WITH BAELDUNG (/PARTNERS/WORK-WITH-US)
EBOOKS (/LIBRARY/)
FAQ (/LIBRARY/FAQ)

BAELDUNG PRO (/MEMBERS/)

TERMS OF SERVICE (/TERMS-OF-SERVICE)


PRIVACY POLICY (/PRIVACY-POLICY)
COMPANY INFO (/BAELDUNG-COMPANY-INFO)
CONTACT (/CONTACT)

PRIVACY MANAGER

12 of 12 07/03/25, 4:00 pm

You might also like