Your guide to the standard Java functional interfaces

Signpost with the words Consumer and Supplier

Last week I wrote about functional interfaces and how to use them as targets for lambda expressions. I showed you how we create a variable of a functional interface type and assign a lambda expression to it. Today I want to help you make sense of the set of standard functional interfaces.

Stream processing is a big part of Java 8 and above. Stream processing makes it easy to process large data sets without writing code to iterate through collections by ourselves. Stream processing uses lambda expressions and functional interfaces extensively. These standard functional interfaces are packaged in the java.util.function package.

At first, the set of standard functional interfaces looks daunting. There are 43 of them! And they have complex and confusing names like ToLongBiFunctionObjDoubleConsumer and LongBinaryOperator.

Never fear, help is here!

Five Groups

The secret to understanding these functional interfaces is that they consist of five groups. The grouping tells you what the functional interface does:

  • Consumer consumes an input and returns void.
  • Supplier produces values.
  • Predicate does a test and returns a boolean.
  • Function is a function that processes some arguments and returns a result.
  • An Operator is similar to a Function: it processes some arguments and returns a result. An Operator is a specialization of Function where the arguments and return value are of the same type.

Hints

When looking for a suitable interface, or trying to understand what is needed by some stream processing method, all you need to do is look at the last word in the interface name. It will be one of the five groups and will tell you what it does (consume, produce, test or run a function).

The first words in the interface name will tell you what type of data it works with (BooleanDoubleIntLong or Obj for objects), and whether it takes one or two arguments (UnaryBi or Binary).

That’s it! It’s as simple as that.

Example using Consumer interfaces

Let’s look at the Consumer interfaces as an example of how you interpret the names.

The Consumer interfaces all return void. They can either take one parameter (unary) or two parameters (binary). They are also specialised as either taking generic parameters, primitive parameters or a combination of a generic and a primitive.

NameName / type of arguments
ConsumerOne generic object argument
BiConsumerTwo generic object arguments
IntConsumerOne int argument
LongConsumerOne long argument
DoubleConsumerOne double argument
ObjIntConsumerOne generic and one int argument
ObjLongConsumerOne generic and one long argument
ObjDoubleConsumerOne generic and one double argument

Summary

The functional interfaces follow an extensible naming convention.

  • The last word in the interface name tells you the basic operation. This will either be ConsumerSupplierPredicateFunction or Operator.
  • If you see Bi or Binary, then you know the functional interface takes two parameters.
  • If it says Unary, it takes one parameter.
  • If you see IntLong, or Double, then you know that it’s a primitive specialization.
  • If you see IntLong, or Double and Obj, then you know that it takes a primitive and a generic as parameters.
  • If you see To in the name, then you know that it converts from one type to another.

Why do we need them?

We can create our own functional interfaces to use as targets for lambdas. Or we can use a suitable interface from the java.util.function package.

A best practice is always to use one of the standard functional interfaces, if it fits your purpose.

Leave a Comment

Your email address will not be published. Required fields are marked *