Reputation: 2841
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.
Upvotes: 3
Views: 2043
Reputation: 2809
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.
Upvotes: 2
Reputation: 198023
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.
Upvotes: 3