Function.identity() and UnaryOperator.identity()
Perhaps the simplest to understand is Function.identity() . This provides a function that simply returns
its argument. It is very useful in testing, when you want to test something that takes a function and acts on
it. This can also be useful as a placeholder function: say that you have five types, and with four of them, you
want to perform some transformation; with the fifth, you don't want to perform a transformation. In that
case, you can maintain the symmetry of your codebase and avoid using null (and NullPointerException s)
by using this method. 10 This method is also available under the UnaryOperator type, so that you can keep
using that Function subclass and save yourself some typing.
Function.compose and Function.andThen
These two default methods provide the ability to take on functionality before and after a given function. The
methods allow you to create a pipeline effect in a rather fluent API, and are especially useful for converting
the types of a function to what you want them to be; you can prepend or append conversion functions inline.
If you want to change the result type, use andThen . Given the function X->Y , andThen allows you to
create X->Z by specifying a function that takes the result, Y , and produces a Z . The argument, X , will be fed to
the instance function, and then the results of the instance function, Y , are fed into the argument function.
As an example, consider this series of method calls:
Function<Integer,String> f = Integer::toString;
f = f.andThen("10"::concat).andThen(Integer::parseInt);
These will create a function that will turn an int into a String , and then prepend “10,” and then parse
the value back out into an int . What started as an int->String type is now an int(->String->String)-
>int , returned in Java types as an int->int .
If you want to change the argument type, use compose . This method should have been called butFirst ,
because it works just like andThen , but on the front end instead of the back end. Given the function X->Y ,
butFirst allows you to create W->Y by specifying a function that takes some W and produces an X . The result of
the specified function is fed as the argument to the instance function. We can specify the previous example
“backwards” by doing this:
Function<String,Integer> f = Integer::parseInt;
f = f.compose("10"::concat).compose(Integer::toString);
The Java SDK does not provide a Consumer.compose to modify the accepted type, 11 but there is a
Consumer.andThen . Since the consumer takes in an argument but produces no result, all that andThen does
is add additional processing; given two consumers of the same type, it provides a single consumer that
executes both the given consumers. This lets you add additional processing to the end of your stream of
processing. It is particularly useful for adding logging or other notifications into your consumer.
10 See “Don't Use Null” below.
11 See the FunJava project's Functions class: it provides a compose method that takes a Function and a Consumer and
provides a Consumer .