Why can't we compose Function and Consumer just like we compose functions?
Function<Integer, String> a = Object::toString;
Consumer<String> b = x -> System.out.println(x);
Consumer<Integer> composed = a.andThen(b);
This seems like an obvious improvisation to the existing Function interface. Is there any reason why this capability was avoided in Java 8?
Also, is it a common practice to use an extended implementation of Function as follows?
interface FunctionImproved<T, R> extends Function<T, R> {
default Consumer<T> andThen(Consumer<R> consumer) {
return x -> consumer.accept(apply(x));
}
}
The andThen method on a Function<A0, A1> instance expects a Function<A1, A2>, not a Consumer<A1>:
a.andThen(string -> {
b.accept(string);
return string;
});
It has a side-effect since our "consumer" (actually, it's a Function<A1, A1>) will be returning an ignored value from the previous Function<Integer, String> a function. It's not what we want.
There is a different way to compose a Function<A0, A1> and a Consumer<A1>:
Consumer<Integer> c = i -> b.accept(a.apply(i));
which can be generalised to a utility method:
<A0, A1> Consumer<A0> functionAndThenConsumer(Function<A0, A1> f, Consumer<A1> c) {
return i -> c.accept(f.apply(i));
}
Is it a common practice to use an extended implementation of
Function?
There are two options:
Having static methods that do some conversions between functional interfaces (like I did).
Extending these standard functional interfaces with default methods (like you did). Just choose a right meaningful name, not like FunctionImproved.
Functions and Consumers serve two different masters (in a sense).
BiFunction) types and produces a third type, which may be either the first or second type.BiConsumer) types, and produces no output.In both cases, there should be a discrete and single operation being done, which would make it easier to reason about the operation's thread safety.
It looks like you're trying to do a simple mapping of one type to another (String to Integer), then call an operation on it. If your elements were in a Stream, this could be written as:
elements.map(Objects::toString).forEach(System.out::println);
...and if it were in a collection or IntStream, you could use this instead:
elements.forEach(System.out::println);
Ultimately what you're attempting may be a solution in search of a problem. Once you're clearer as to the role that functions and consumers actually play, attempts at compositions like the above become less necessary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With