13.4 Overview of Built-In Functional Interfaces
Earlier in this chapter, specialized interfaces (including some functional ones) were mentioned that are readily available in the Java SE Platform API (p. 678). To facilitate defining common functions with lambda expressions, the Java SE Platform API also provides a versatile set of functional interfaces for this purpose.
The main support for functional interfaces is found in the java.util.function package. The general-purpose generic functional interfaces shown in Table 13.1 represent the four basic operations that are commonly implemented by functions: to get a value (Supplier<T>), to test a predicate (Predicate<T>), to accept a value but not return a result (Consumer<T>), and to apply a function to a value in order to compute a new result (Function<T, R>).
The term arity refers to the number of arguments that a method requires as its input. A method is called zero-arity, one-arity, or two-arity, depending on whether the method has zero, one, or two arguments, respectively. Depending on whether the functional method is zero-arity, one-arity, or two-arity, its functional interface is likewise referred to as zero-arity, one-arity, or two-arity, respectively. Note that the arity of the functional interface reflects the arity of its functional method. Particularly for a generic functional interface, the arity should not be confused with the number of type parameters specified for the generic functional interface.
In Table 13.1, except for the Supplier<T> functional interface which has a zero-arity functional method, the functional methods for the other three basic functional interfaces are one-arity methods. Accordingly, the Supplier<T> functional interface is a zero-arity functional interface, whereas the other functional interfaces are onearity functional interfaces.
Table 13.1 Basic Functional Interfaces in the java.util.function Package
Functional interface (T and R are type parameters) | Functional method | Function | Arity of function type |
Supplier<T> | get: () -> T | Provide an instance of a T. | Zero-arity |
Predicate<T> | test: T -> boolean | Evaluate a predicate on a T. | One-arity |
Consumer<T> | accept: T -> void | Perform action on a T. | One-arity |
Function<T, R> | apply: T -> R | Transform a T to an R. | One-arity |
It is important to understand the abstract operations that the basic functional interfaces provide before tackling the specialized versions of these functional interfaces in the java.util.function package. Since the package provides a wide range of functional interfaces for various purposes, defining new ones should hardly be necessary.
The complete list of all built-in functional interfaces in the java.util.function package is given in Table 13.2. The table also shows any default methods that a built-in functional interface defines. The idea is not to memorize them all, but to understand how they are categorized according to the four basic functional interfaces in Table 13.1. The specialized versions of the basic functional interfaces are derived by combining one or more of the following three forms:
- Two-arity specializations of the basic functional interfaces
These functional interfaces (BiPredicate<T,U>, BiConsumer<T,U>, BiFunction<T,U,R>) are two-arity specialized counterparts to the corresponding basic functional interface, except for the Supplier<T> interface which does not have a two-arity specialization.
- Extended versions of the Function<T,R> and BiFunction<T,U,R> interfaces
The functional interfaces UnaryOperator<T> and BinaryOperator<T> extend the Function<T,T> and BiFunction<T,T,T> interfaces, respectively. As their names imply, the two specialized functional interfaces UnaryOperator<T> and BinaryOperator<T> are one-arity and two-arity functional interfaces as their superinterfaces, respectively, where the parameters and the result in each have the same type.
- Primitive type specializations of generic functional interfaces
The primitive type specializations avoid excessive boxing and unboxing of primitive values when such values are used as objects.
The primitive type counterparts are specializations of each generic functional interface where one or more type parameters are replaced by a primitive type. Primitive type specializations primarily involve one or more of the primitive types int, long, or double.
The naming scheme uses one or more prefixes in front of the name of a primitive type functional interface to indicate its function type—that is, the type of the parameters and that of the result. For example, IntPredicate has the function type int -> boolean, whereas IntToDoubleFunction has the function type int -> double, and LongBinaryOperator has the function type (long, long) -> long.
Table 13.2 Built-In Functional Interfaces in the java.util.function Package
Functional interface (T, U, and R are type parameters) | Functional method | Default methods unless otherwise indicated |
Supplier<T> | get: () -> T | – |
IntSupplier | getAsInt: () -> int | – |
LongSupplier | getAsLong: () -> long | – |
DoubleSupplier | getAsDouble: () -> double | – |
BooleanSupplier | getAsBoolean: () -> boolean | – |
Predicate<T> | test: T -> boolean | and(), or(), negate(), static isEqual(), static not() |
IntPredicate | test: int -> boolean | and(), or(), negate() |
LongPredicate | test: long -> boolean | and(), or(), negate() |
DoublePredicate | test: double -> boolean | and(), or(), negate() |
BiPredicate<T, U> | test: (T, U) -> boolean | and(), or(), negate() |
Consumer<T> | accept: T -> void | andThen() |
IntConsumer | accept: int -> void | andThen() |
LongConsumer | accept: long -> void | andThen() |
DoubleConsumer | accept: double -> void | andThen() |
BiConsumer<T, U> | accept: (T, U) -> void | andThen() |
ObjIntConsumer<T> | accept: (T, int) -> void | – |
ObjLongConsumer<T> | accept: (T, long) -> void | – |
ObjDoubleConsumer<T> | accept: (T, double) -> void | – |
Function<T, R> | apply: T -> R | compose(), andThen(), static identity() |
IntFunction<R> | apply: int -> R | – |
LongFunction<R> | apply: long -> R | – |
DoubleFunction<R> | apply: double -> R | – |
ToIntFunction<T> | applyAsInt: T -> int | – |
ToLongFunction<T> | applyAsLong: T -> long | – |
ToDoubleFunction<T> | applyAsDouble: T -> double | – |
IntToLongFunction | applyAsLong: int -> long | – |
IntToDoubleFunction | applyAsDouble: int -> double | – |
LongToIntFunction | applyAsInt: long -> int | – |
LongToDoubleFunction | applyAsDouble: long -> double | – |
DoubleToIntFunction | applyAsInt: double -> int | – |
DoubleToLongFunction | applyAsLong: double -> long | – |
BiFunction<T, U, R> | apply: (T, U) -> R | andThen() |
ToIntBiFunction<T, U> | applyAsInt: (T, U) -> int | – |
ToLongBiFunction<T, U> | applyAsLong: (T, U) -> long | – |
ToDoubleBiFunction<T, U> | applyAsDouble: (T, U) -> double | – |
UnaryOperator<T> extends Function<T,T> | apply: T -> T | compose(), andThen(), static identity() |
IntUnaryOperator | applyAsInt: int -> int | compose(), andThen() |
LongUnaryOperator | applyAsLong: long -> long | compose(), andThen() |
DoubleUnaryOperator | applyAsDouble: double -> double | compose(), andThen() |
BinaryOperator<T> extends BiFunction<T,T,T> | apply: (T, T) -> T | andThen(), static maxBy(), static minBy() |
IntBinaryOperator | applyAsInt: (int, int) -> int | – |
LongBinaryOperator | applyAsLong: (long, long) -> long | – |
DoubleBinaryOperator | applyAsDouble: (double, double) -> double | – |
The columns in Table 13.3 list the built-in functional interfaces in the java.util.function package according to each category of basic functional interface.
Table 13.3 Summary of Built-In Functional Interfaces