Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to use BiConsumers as simply as Consumers?

This is just a theorical question with no concrete application.

I have the following method which I will not touch. It could (if possible at all) be used as a BiConsumer.

void doSmallThing(A a, B b) {
  // do something with a and b.
}

void doBigThing(List<A> as, B b) {
  // What to do?
}

How can I iterate on as while keeping b constant and use this::doSmallThing in doBigThing?

Of course the following doesn't work.

void doBigThing(List<A> as, B b) {
  as.stream()
  .forEach(this::doSmallThing);
}

The following works nice and is actually what I use everyday.

void doBigThing(List<A> as, B b) {
  as.stream()
  .forEach(a -> doSmallThing(a, b));
}

The following also works well, but is a bit more tricky.

Consumer<A> doSmallThingWithFixedB(B b) {
  return (a) -> doSmallThing(a, b);
}

void doBigThing(List<A> as, B b) {
  as.stream()
  .forEach(doSmallThingWithFixedB(b))
}

But all of those solutions don't get the simplicity of the Consumer case. So is there anything simple that exists for BiConsumer?

like image 750
Olivier Grégoire Avatar asked Jun 25 '15 09:06

Olivier Grégoire


2 Answers

You want to "bind" the function argument. Unfortunately there's no built-in mechanism to do this in Java 8 (except binding the object for instance methods like this::). You may generalize your doSmallThingWithFixedB method like this:

public class Bind {
    public static <A, B> Consumer<A> bindLast(BiConsumer<A, B> fn, B b) {
        return a -> fn.accept(a, b);
    }

    public static <A, B> Consumer<B> bindFirst(BiConsumer<A, B> fn, A a) {
        return b -> fn.accept(a, b);
    }
}

And use:

void doBigThing(List<A> as, B b) {
  as.stream()
    .forEach(Bind.bindLast(this::doSmallThing, b));
}

Probably there's some third-party library which already contains such methods. However using explicit lambda seems ok for me. You should not try to express everything with method references.

like image 95
Tagir Valeev Avatar answered Oct 27 '22 00:10

Tagir Valeev


BiConsumers are used when iterating over Map entries for example:

Map<A, B> map = ...;
map.forEach(this::doSomething);

Stream.collect() also takes BiConsumers as arguments, but it's used less often than an iteration on map entries.

like image 32
JB Nizet Avatar answered Oct 26 '22 23:10

JB Nizet