3.4. Returning Functions
In a functional programming language, functions are first-class citizens. Just like you can pass numbers to methods and have methods that produce numbers, you can have arguments and return values that are functions. This sounds abstract, but it is very useful in practice. Java is not quite a functional language because it uses functional interfaces, but the principle is the same. You have seen many methods that accept functional interfaces. In this section, we consider methods whose return type is a functional interface.
Consider again image transformations. If you call
Image brightenedImage = transform(image, Color::brighter);
the image is brightened by a fixed amount. What if you want it even brighter, or not quite so bright? Could you supply the desired brightness as an additional parameter to transform?
Image brightenedImage = transform(image, (c, factor) -> c.deriveColor(0, 1, factor, 1), // Brighten c by factor 1.2); // Use a factor of 1.2
One would have to overload transform:
public static <T> Image transform(Image in, BiFunction<Color, T> f, T arg)
That can be made to work (see Exercise 6), but what if one wants to supply two arguments? Or three? There is another way. We can make a method that returns the appropriate UnaryOperator<Color>, with the brightness set:
public static UnaryOperator<Color> brighten(double factor) { return c -> c.deriveColor(0, 1, factor, 1); }
Then we can call
Image brightenedImage = transform(image, brighten(1.2));
The brighten method returns a function (or, technically, an instance of a functional interface). That function can be passed to another method (here, transform) that expects such an interface.
In general, don’t be shy to write methods that produce functions. This is useful to customize the functions that you pass to methods with functional interfaces. For example, consider the Arrays.sort method with a Comparator argument. There are many ways of comparing values, and you can write a method that yields a comparator for your needs—see Exercise 7. Then you can call Arrays.sort(values, comparatorGenerator(customization arguments)).