Is there a way I can have the following action actually be two actions in one?
static int process(String[] cmd, File dir, Consumer<? super String> action) throws IOException {
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.directory(dir);
pb.redirectErrorStream(true);
Stopwatch sw = Stopwatch.createStarted();
Process p = pb.start();
int exit = -1;
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
br.lines().forEach(action);
} finally {
log.debug("Ending process {} with exist code {} in time {}", pb.command(),
exit = p.destroyForcibly().exitValue(), sw);
}
return exit;
}
So the Consumer<? super String> action I typically specify it to be log::debug or a custom function like AtomicReference::set however what if I wanted to be both, or a third? How can I have the action perform a few independent actions?
I know I could just create a custom Consumer that does whatever I want, but I think it would be very convenient
if I could almost treat the action like an nvarargs equivalent, but for functions/actions.
It's easy enough to make a Consumer that delegates to other consumers. You just write
str -> {
action1(str);
action2(str);
// etc
}
since you don't have to use method references, you can always use lambdas. Writing your Consumer in that style also tends to be more readable than trying to combine them in other ways, though other ways are possible.
There is a few alternatives here, some of which have already been mention by others.
Perhaps the most lambda like approach would be to use the consumers own aggregation method andThen (defined in Consumer itself):
Stream<X> exes = ....
exes.forEach(action1
.andThen(action2)
.andThen(action3)
.andThen(...)
.andThen(actionN));
Where all action? are declared as Consumer<X> or Consumer<? super X>.
Without looking at the docs to confirm. I guess that actions 1 to N are executed one right after the other on the same thread for every element in the stream.
Another possibility here is to use peek that basically passes the elements of the stream to an action without consuming them as it returns a stream that would contain the same elements.
Stream<X> exes = ...
exes.peek(action1)
.peek(action2)
.peek(action3)
.peek(...)
.forEach(actionN);
Without looking at the docs I bet that:
forEach (or count, empty, etc.) in order to get the different peeks executed. I reckon that you may want to use Consumer.andThen based on the content of your question however peek seems like a plausible solution if there is some action whose execution is not central/main to the task at hand but rather like a desirable side-effect.
Perhaps you want to pass a modified stream that would "spy" on what objects are being processed later by the code that the stream reference has been passed to. In that case peek is the better if not the only option.
Of course you could also do a combination of both.
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