Java’s functional interfaces were introduced back in Java 8, so they’ve been around for a long time, at this point.
One of the best insights I have read about these interfaces is in a Stack Overflow answer written by Stuart Marks:
The reason you’re having difficulty grasping the meaning of functional interfaces such as those in
java.util.functionis that the interfaces defined here do not have any meaning! They are present primarily to represent structure, not semantics.
He goes on to contrast these functional interfaces with an interface such as the
List interface. For
List, it is clear what data structure is being represented: it’s a list, of course! And it is clear what you can do with that list: you can add elements to it, you can iterate over it, and so on…
But what can you do with
Consumer<T> and its
accept(T t) method? A method which, as the JavaDoc says:
Performs this operation on the given argument.
What is “this operation”? That’s quite abstract, compared to
Stuart Marks goes on to clarify that these functional interfaces:
are interfaces that merely represent the structure of a function, such as the number of arguments, the number of return values, and (sometimes) whether an argument or return value is a primitive.
That, for me, is an extremely helpful insight. The functional interface provides the structure. You provide the semantics. And because its a functional interface, you can use Java’s lambda expressions as implementations - and that gives you a lot of flexibility in how they can be used. For example, you can assign a lambda function to a variable.
Some example code, with notes, is shown below.
Just because you can pass functions around in this way, doesn’t mean you always should.
Consider the incredibly simple Java class
And the following class, which stores three functions in a map:
We can execute any of these functions as follows:
The semantics are provided by me in those three example functions, stored in the map, and called using the
apply() method from the