4 available functional interfaces and examples with Stream API

Tram Ho

As the previous two articles have introduced, one of the conditions of functional programming is the ability to pass one function into another. Java has done this by declaring the functional interface and the corresponding pass implementation.

However, if you have to define a new functional interface each time, it’s too verbose. With the common “shape” methods, Java has predefined quite a few functional interfaces to save you the need to rewrite. 4 basic ones include ConsumerFunctionPredicate and Supplier, You must have heard it before, let’s find out more through this article.

1. Four basic functional interfaces

Actually, when learning about this functional interface, just remember its structure. Eg Consumer<T>, What parameters to accept, what data type to return.

Besides, most of these functional interfaces use generics. Nor does it need to remember what generics param is, it can be deduced from the meaning.

1.1. Consumer<T>

Get a param of type T, returns nothing. In simple words, they only like to receive but do not give away.

Usually found in functions that handle elements, such as getting an element and doing something.

1.2. Function<T, R>

Get a parameter T, returns a value of type R.

Used to transform one value into another (map 1 with 1).

1.3. Predicate<T>

Get a parameter T and return boolean.

Used to check if the element satisfies the condition or not.

1.4. Supplier<T>

This man has blood type O. He only knows how to give, but he doesn’t need to receive it in return.

This functional interface takes no parameters, but returns a value.

Usually used to generate infinite streams.

2. Example of each type in Stream API

The above 4 functional interfaces are quite basic, but you will wonder where is it used? The answer is used in Stream API operations.

Here I will explain basically how the stream works:

  • Gets a stream (like a continuous stream of elements) from an array or collection
  • The elements of the stream will go through many operations (like that filter).
  • Each operation will transform each element, depending on the type of operation (map, filter, …)
  • The final result will be processed

Example code for easy understanding.

Here map()filter() and forEach() are stream operations. Each type will perform a different function, and it uses the corresponding functional interface types:

  • map() to transform the element 1-1, so it uses Function<T, R>
  • filter() to filter out matching elements, it uses Predicate<T>
  • forEach() just use print out, don’t return anything, so it uses Consumer<T>

Usually people write lambda form for short, and always write in operation. If you want you can still separate, like on the example in part 1.

Oh how many apply()test(),… inside functional interface, where to do it?

Those methods will be implicitly implemented by the Stream API. You just need to put the lambda into the operation, the code inside of that operation will know and call the corresponding method inside.


Okay the post is quite long, I will stop here. In the next post we will continue to come to other available functional interfaces, with more “advanced” forms.

Share the news now