3.3. Choosing a Functional Interface
In most functional programming languages, function types are structural. To specify a function that maps two strings to an integer, you use a type that looks something like Function2<String, String, Integer> or (String, String) -> int. In Java, you instead declare the intent of the function, using a functional interface such as Comparator<String>. In the theory of programming languages this is called nominal typing.
Of course, there are many situations where you want to accept “any function” without particular semantics. There are a number of generic function types for that purpose (see Table 3-1), and it’s a very good idea to use one of them when you can.
Table 3-1 Common Functional Interfaces
Functional Interface |
Parameter Types |
Return Type |
Abstract Method Name |
Description |
Other Methods |
Runnable |
none |
void |
run |
Runs an action without arguments or return value |
|
Supplier<T> |
none |
T |
get |
Supplies a value of type T |
|
Consumer<T> |
T |
void |
accept |
Consumes a value of type T |
chain |
BiConsumer<T, U> |
T, U |
void |
accept |
Consumes values of types T and U |
chain |
Function<T, R> |
T |
R |
apply |
A function with argument of type T |
compose, andThen, identity |
BiFunction<T, U, R> |
T, U |
R |
apply |
A function with arguments of types T and U |
andThen |
UnaryOperator<T> |
T |
T |
apply |
A unary operator on the type T |
compose, andThen, identity |
BinaryOperator<T> |
T, T |
T |
apply |
A binary operator on the type T |
andThen |
Predicate<T> |
T |
boolean |
test |
A Boolean-valued function |
and, or, negate, isEqual |
BiPredicate<T, U> |
T, U |
boolean |
test |
A Boolean-valued function with two arguments |
and, or, negate |
For example, suppose you write a method to process files that match a certain criterion. Should you use the descriptive java.io.FileFilter class or a Predicate<File>? I strongly recommend that you use the standard Predicate<File>. The only reason not to do so would be if you already have many useful methods producing FileFilter instances.
Consider another example. We want to transform images, applying a Color -> Color function to each pixel. For example, the brightened image in Figure 3-1 is obtained by calling
Image brightenedImage = transform(image, Color::brighter);
Figure 3-1 The original and transformed image
There is a standard functional interface for this purpose: UnaryOperator<Color>. That is a good choice, and there is no need to come up with a ColorTransformer interface.
Here is the implementation of the transform method. Note the call to the apply method.
public static Image transform(Image in, UnaryOperator<Color> f) { int width = (int) in.getWidth(); int height = (int) in.getHeight(); WritableImage out = new WritableImage(width, height); for (int x = 0; x < width; x++) for (int y = 0; y < height; y++) out.getPixelWriter().setColor(x, y, f.apply(in.getPixelReader().getColor(x, y))); return out; }
Table 3-2 lists the 34 available specializations for primitive types int, long, and double. Use the specializations when you can to reduce autoboxing.
Table 3-2 Functional Interfaces for Primitive Types: p, q is int, long, double; P, Q is Int, Long, Double
Functional Interface |
Parameter Types |
Return Type |
Abstract Method Name |
BooleanSupplier |
none |
boolean |
getAsBoolean |
PSupplier |
none |
p |
getAsP |
PConsumer |
p |
void |
accept |
ObjPConsumer<T> |
T, p |
void |
accept |
PFunction<T> |
p |
T |
apply |
PToQFunction |
p |
q |
applyAsQ |
ToPFunction<T> |
T |
p |
applyAsP |
ToPBiFunction<T, U> |
T, U |
p |
applyAsP |
PUnaryOperator |
p |
p |
applyAsP |
PBinaryOperator |
p, p |
p |
applyAsP |
PPredicate |
p |
boolean |
test |
Sometimes, you need to supply your own functional interface because there is nothing in the standard library that works for you. Suppose you want to modify colors in an image, allowing users to specify a function (int, int, Color) -> Color that computes a new color depending on the (x, y) location in the image. In that case, you can define your own interface:
@FunctionalInterface public interface ColorTransformer { Color apply(int x, int y, Color colorAtXY); }