The java.util.function Package
The java.util.function Package
java.util.function. The interfaces in java.util.function fall into four categories:
(1) consumers - Consumers take a generic argument and return nothing
(2) suppliers - Suppliers take no arguments and return a value
(3) predicates - Predicates take an argument and return a boolean
(4) functions - Functions take a single argument and return a value.
For each of the basic interfaces, there are several related ones.
For example, Consumer has variations customized for primitive types (IntConsumer, LongConsumer, and DoubleConsumer) and
a variation (BiConsumer) that takes two arguments and returns void.
Although by definition the interfaces in this chapter only contain a single abstract method, most also include additional methods that are either static or default. Becoming familiar with these methods will make your job as a developer easier.
Consumers
Problem
You want to write lambda expressions that implement the java.util.function.Consumer package.
Solution
Implement the void accept(T t) method using a lambda expression or a method reference.
Methods in java.util.function.Consumer
The accept method takes a generic argument and returns void. One of the most frequently used examples of a method that takes a Consumer as an argument is the default forEach method in java.util.Iterable, shown in
The forEach method in Iterable
All linear collections implement this interface by performing the given action for each element of the collection, as in Example 2-3.
Example 2-3. Printing the elements of a collection
Anonymous inner class implementation
Expression lambda
Method reference
The lambda expression conforms to the signature of the accept method, because it takes a single argument and returns nothing. The println method in PrintStream, accessed here via System.out, is compatible with Consumer. Therefore, either can be used as the target for an argument of type Consumer.
The java.util.function package also contains primitive variations of Consumer<T>, as well as a two-argument version. See Table 2-1 for details.
| Interface | Single abstract method |
|---|---|
|
|
|
|
|
|
|
|
Consumers are expected to operate via side effects, as shown in Recipe 2.3.
BiConsumer interface has an accept method that takes two generic arguments, which are assumed to be of different types. BiConsumer where the second argument is a primitive.ObjIntConsumer, whose accept method takes two arguments, a generic and and an int.ObjLongConsumer and 3) ObjDoubleConsumer are defined similarly.Other uses of the Consumer interface in the standard library include:
Stream.forEach(Consumer<? super T> action)Performs an action for each element of the stream.1 The Stream.forEachOrdered method is similar, accessing elements in encounter order.
Stream.peek(Consumer<? super T> action)Returns a stream with the same elements as the existing stream, first performing the given action. This is a very useful technique for debugging (see Recipe 3.5 for an example).
2) Suppliers :
Discussion
The java.util.function.Supplier interface is particularly simple. It does not have any static or default methods. It contains only a single, abstract method, T get().
Implementing Supplier means providing a method that takes no arguments and returns the generic type. As stated in the Javadocs, there is no requirement that a new or distinct result be returned each time the Supplier is invoked.
One simple example of a Supplier is the Math.random method, which takes no arguments and returns a double. That can be assigned to a Supplier reference and invoked at any time, as in Example 2-4.
Example 2-4. Using Math.random() as a Supplier
Anonymous inner class implementation
Expression lambda
Method reference
The single abstract method in DoubleSupplier is getAsDouble, which returns a double. The other associated Supplier interfaces in the java.util.function package are shown in Table 2-2.
| Interface | Single abstract method |
|---|---|
|
|
|
|
|
|
|
|
One of the primary use cases for Suppliers is to support the concept of deferred execution. The info method in java.util.logging.Logger takes a Supplier, whose get method is only called if the log level means the message will be seen (shown in detail in Recipe 5.7). This process of deferred execution can be used in your own code, to ensure that a value is retrieved from a Supplier only when appropriate.
Another example from the standard library is the orElseGet method in Optional, which also takes a Supplier. The Optional class is discussed in Chapter 6, but the short explanation is that an Optional is a nonnull object that either wraps a value or is empty. It is typically returned by methods that may reasonably expect to have no result, like finding a value in an empty collection.
To see how that might work, consider searching for a name in a collection, as shown in Example 2-5.
Example 2-5. Finding a name from a collection
The findFirst method on Stream returns the first encountered element in an ordered stream.2 Since it’s possible to apply a filter so there are no elements remaining in the stream, the method returns an Optional. That Optional either contains the desired element, or is empty. In this case, none of the names in the list pass the filter, so the result is an empty Optional.
The orElse method on Optional returns either the contained element, or a specified default. That’s fine if the default is a simple string, but can be wasteful if processing is necessary to return a value.
In this case, the returned value shows the complete list of names in comma-separated form. The orElse method creates the complete string, whether the Optional contains a value or not.
The orElseGet method, however, takes a Supplier as an argument. The advantage is that the get method on the Supplier will only be invoked when the Optional is empty, so the complete name string is not formed unless it is necessary.
Other examples from the standard library that use Suppliers include:
The
orElseThrowmethod inOptional, which takes aSupplier<X extends Exception>. TheSupplieris only executed if an exception occurs.Objects.requireNonNull(T obj, Supplier<String> messageSupplier)only customizes its response if the first argument is null.CompletableFuture.supplyAsync(Supplier<U> supplier)returns aCompletableFuturethat is asynchronously completed by a task running with the value obtained by calling the givenSupplier.The
Loggerclass has overloads for all its logging methods that takes aSupplier<String>rather than just a string (used as an example in Recipe 5.7).
Predicates
Solution
Implement the boolean test(T t) method in the Predicate interface using a lambda expression or a method reference.
Discussion
Predicates are used primarily to filter streams. Given a stream of items, the filter method in java.util.stream.Stream takes a Predicate and returns a new stream that includes only the items that satisfy the given predicate.
The single abstract method in Predicate is boolean test(T t), which takes a single generic argument and returns true or false. The complete set of methods in Predicate, including state and defaults, is given in Example 2-6.
Example 2-6. Methods in java.util.function.Predicate
Say you have a collection of names and you want to find all the instances that have a particular length. Example 2-7 shows an example of how to use stream processing to do so.
Example 2-7. Finding strings of a given length
Alternatively, perhaps you want only the names that start with a particular string, as in Example 2-8.
Example 2-8. Finding strings that start with a given string
These can be made more general by allowing the condition to be specified by the client. Example 2-9 shows a method to do that.
Example 2-9. Finding strings that satisfy an arbitrary predicate
Chapter 2. The java.util.function Package
The previous chapter discussed the basic syntax of lambda expressions and method references. One basic principle is that for either, there is always a context. Lambda expressions and method references are always assigned to functional interfaces, which provide information about the single abstract method being implemented.
While many interfaces in the Java standard library contain only a single, abstract method and are thus functional interfaces, there is a new package that is specifically designed to contain only functional interfaces that are reused in the rest of the library. That package is called java.util.function.
The interfaces in java.util.function fall into four categories: (1) consumers, (2) suppliers, (3) predicates, and (4) functions. Consumers take a generic argument and return nothing. Suppliers take no arguments and return a value. Predicates take an argument and return a boolean. Functions take a single argument and return a value.
For each of the basic interfaces, there are several related ones. For example, Consumer has variations customized for primitive types (IntConsumer, LongConsumer, and DoubleConsumer) and a variation (BiConsumer) that takes two arguments and returns void.
Although by definition the interfaces in this chapter only contain a single abstract method, most also include additional methods that are either static or default. Becoming familiar with these methods will make your job as a developer easier.
2.1 Consumers
Discussion
The java.util.function.Consumer interface has as its single, abstract method, void accept(T t). See Example 2-1.
Example 2-1. Methods in java.util.function.Consumer
The accept method takes a generic argument and returns void. One of the most frequently used examples of a method that takes a Consumer as an argument is the default forEach method in java.util.Iterable, shown in Example 2-2.
Example 2-2. The forEach method in Iterable
All linear collections implement this interface by performing the given action for each element of the collection, as in Example 2-3.
Example 2-3. Printing the elements of a collection
The lambda expression conforms to the signature of the accept method, because it takes a single argument and returns nothing. The println method in PrintStream, accessed here via System.out, is compatible with Consumer. Therefore, either can be used as the target for an argument of type Consumer.
The java.util.function package also contains primitive variations of Consumer<T>, as well as a two-argument version. See Table 2-1 for details.
| Interface | Single abstract method |
|---|---|
|
|
|
|
|
|
|
|
TIP
Consumers are expected to operate via side effects, as shown in Recipe 2.3.
The BiConsumer interface has an accept method that takes two generic arguments, which are assumed to be of different types. The package contains three variations on BiConsumer where the second argument is a primitive. One is ObjIntConsumer, whose accept method takes two arguments, a generic and and an int. ObjLongConsumer and ObjDoubleConsumer are defined similarly.
Other uses of the Consumer interface in the standard library include:
Optional.ifPresent(Consumer<? super T> consumer)If a value is present, invoke the specified consumer. Otherwise do nothing.
Stream.forEach(Consumer<? super T> action)Performs an action for each element of the stream.1 The
Stream.forEachOrderedmethod is similar, accessing elements in encounter order.Stream.peek(Consumer<? super T> action)Returns a stream with the same elements as the existing stream, first performing the given action. This is a very useful technique for debugging (see Recipe 3.5 for an example).
See Also
The andThen method in Consumer is used for composition. Function composition is discussed further in Recipe 5.8. The peek method in Stream is examined in Recipe 3.5.
2.2 Suppliers
Discussion
The java.util.function.Supplier interface is particularly simple. It does not have any static or default methods. It contains only a single, abstract method, T get().
Implementing Supplier means providing a method that takes no arguments and returns the generic type. As stated in the Javadocs, there is no requirement that a new or distinct result be returned each time the Supplier is invoked.
One simple example of a Supplier is the Math.random method, which takes no arguments and returns a double. That can be assigned to a Supplier reference and invoked at any time, as in Example 2-4.
Example 2-4. Using Math.random() as a Supplier
The single abstract method in DoubleSupplier is getAsDouble, which returns a double. The other associated Supplier interfaces in the java.util.function package are shown in Table 2-2.
| Interface | Single abstract method |
|---|---|
|
|
|
|
|
|
|
|
One of the primary use cases for Suppliers is to support the concept of deferred execution. The info method in java.util.logging.Logger takes a Supplier, whose get method is only called if the log level means the message will be seen (shown in detail in Recipe 5.7). This process of deferred execution can be used in your own code, to ensure that a value is retrieved from a Supplier only when appropriate.
Another example from the standard library is the orElseGet method in Optional, which also takes a Supplier. The Optional class is discussed in Chapter 6, but the short explanation is that an Optional is a nonnull object that either wraps a value or is empty. It is typically returned by methods that may reasonably expect to have no result, like finding a value in an empty collection.
To see how that might work, consider searching for a name in a collection, as shown in Example 2-5.
Example 2-5. Finding a name from a collection
The findFirst method on Stream returns the first encountered element in an ordered stream.2 Since it’s possible to apply a filter so there are no elements remaining in the stream, the method returns an Optional. That Optional either contains the desired element, or is empty. In this case, none of the names in the list pass the filter, so the result is an empty Optional.
The orElse method on Optional returns either the contained element, or a specified default. That’s fine if the default is a simple string, but can be wasteful if processing is necessary to return a value.
In this case, the returned value shows the complete list of names in comma-separated form. The orElse method creates the complete string, whether the Optional contains a value or not.
The orElseGet method, however, takes a Supplier as an argument. The advantage is that the get method on the Supplier will only be invoked when the Optional is empty, so the complete name string is not formed unless it is necessary.
Other examples from the standard library that use Suppliers include:
The
orElseThrowmethod inOptional, which takes aSupplier<X extends Exception>. TheSupplieris only executed if an exception occurs.Objects.requireNonNull(T obj, Supplier<String> messageSupplier)only customizes its response if the first argument is null.CompletableFuture.supplyAsync(Supplier<U> supplier)returns aCompletableFuturethat is asynchronously completed by a task running with the value obtained by calling the givenSupplier.The
Loggerclass has overloads for all its logging methods that takes aSupplier<String>rather than just a string (used as an example in Recipe 5.7).
See Also
Using the overloaded logging methods that take a Supplier is discussed in Recipe 5.7. Finding the first element in a collection is discussed in Recipe 3.9. Completable futures are part of several recipes in Chapter 9, and Optional is the topic of recipes in Chapter 6.
2.3 Predicates
Solution
Implement the boolean test(T t) method in the Predicate interface using a lambda expression or a method reference.
Discussion
Predicates are used primarily to filter streams. Given a stream of items, the filter method in java.util.stream.Stream takes a Predicate and returns a new stream that includes only the items that satisfy the given predicate.
The single abstract method in Predicate is boolean test(T t), which takes a single generic argument and returns true or false. The complete set of methods in Predicate, including state and defaults, is given in Example 2-6.
Say you have a collection of names and you want to find all the instances that have a particular length. Example 2-7 shows an example of how to use stream processing to do so.
Alternatively, perhaps you want only the names that start with a particular string, as in Example 2-8.
Example 2-8. Finding strings that start with a given string
These can be made more general by allowing the condition to be specified by the client. Example 2-9 shows a method to do that.
This is quite flexible, but it may be a bit much to expect the clients to write every predicate themselves. One option is to add constants to the class representing the most common cases, as in Example 2-10.
Example 2-10. Adding constants for common cases
The other advantage to supplying a predicate as an argument is that you can also use the default methods and, or, and negate to create a composite predicate from a series of individual elements.
The test case in Example 2-11 demonstrates all of these techniques.
Other methods in the standard library that use predicates include:
Optional.filter(Predicate<? super T> predicate)If a value is present, and the value matches the given predicate, returns an
Optionaldescribing the value, otherwise returns an emptyOptional.Collection.removeIf(Predicate<? super E> filter)Removes all elements of this collection that satisfy the predicate.
Stream.allMatch(Predicate<? super T> predicate)Returns true if all elements of the stream satisfy the given predicate. The methods
anyMatchandnoneMatchwork similarly.Collectors.partitioningBy(Predicate<? super T> predicate)Returns a
Collectorthat splits a stream into two categories: those that satisfy the predicate and those that do not.
Functions
Discussion
The functional interface java.util.function.Function contains the single abstract method apply, which is invoked to transform a generic input parameter of type T into a generic output value of type R. The methods in Function are shown in Example 2-12.
Example 2-12. Methods in the java.util.function.Function interface
The most common usage of Function is as an argument to the Stream.map method. For example, one way to transform a String into an integer would be to invoke the length method on each instance, as in Example 2-13.
Example 2-13. Mapping strings to their lengths
The complete list of primitive variations for both the input and the output generic types are shown in Table 2-3.
| Interface | Single abstract method |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
java.util.function package defines UnaryOperator for that. As you might expect, there are also interfaces called IntUnaryOperator, DoubleUnaryOperator, and LongUnaryOperator, where the input and output arguments are int, double, and long, respectively. An example of a UnaryOperator would be the reverse method in StringBuilder, because both the input type and the output type are strings.The BiFunction interface is defined for two generic input types and one generic output type, all of which are assumed to be different. If all three are the same, the package includes the BinaryOperator interface. An example of a binary operator would be Math.max, because both inputs and the output are either int, double, float, or long. Of course, the interface also defines interfaces called IntBinaryOperator, DoubleBinaryOperator, and LongBinaryOperator for those situations.3
To complete the set, the package also has primitive variations of BiFunction, which are summarized in Table 2-4.
| Interface | Single abstract method |
|---|---|
|
|
|
|
|
|
While the various Stream.map methods are the primary usages of Function, they do appear in other contexts. Among them are:
Map.computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)If the specified key does not have a value, use the provided
Functionto compute one and add it to aMap.Comparator.comparing(Function<? super T,? extends U> keyExtractor)Discussed in Recipe 4.1, this method generates a
Comparatorthat sorts a collection by the key generated from the givenFunction.Comparator.thenComparing(Function<? super T,? extends U> keyExtractor)An instance method, also used in sorting, that adds an additional sorting mechanism if the collection has equal values by the first sort.
Functions are also used extensively in the Collectors utility class for grouping and downstream collectors.
The andThen and compose methods are discussed in Recipe 5.8. The identity method is simply the lambda expression e -> e. One usage is shown in Recipe 4.3.
Comments
Post a Comment