4

I'm trying to use lambda expressions in Java 8. The type of the expression that I want to write is:

BiConsumer<String, BiConsumer<Boolean,Integer>> cc2;

I should be able to write an expression looking a bit like this:

cc2 = (s,(b,i)) -> { System.out.println(s+b+i); }; // this does not compile

Unfortunately I can't find the exact syntax I must use.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
niedomnie
  • 107
  • 6
  • 1
    [What does lambda with 2 arrows mean in Java 8?](http://stackoverflow.com/q/32820722/2711488) – Holger Jun 30 '16 at 08:25

2 Answers2

8

The type BiConsumer<String, BiConsumer<Boolean,Integer>> declares a BiConsumer, that is to say a consumer that consumes 2, and only 2, arguments. Of those two arguments, one is declared to be of type String, while the other is declared of type BiConsumer<Boolean,Integer>.

As such, this would compile:

BiConsumer<String, BiConsumer<Boolean,Integer>> cc2 = (s, c) -> {};

s would be of type String and c would be of type BiConsumer. The arguments to c would need to be captured and won't be lambda parameters.

That is not what you want though: you actually want to consume 3 arguments, not 2. There is no built-in functional interface for this requirement, but you can make your own:

@FunctionalInterface
interface TriConsumer<A,B,C> {
    void accept(A a, B b, C c);
}

Then, you can use it with

TriConsumer<String,Boolean,Integer> tri = (s, b, i) -> System.out.println(s+b+i);

Alternatively, you can make use of currying by declaring a Function that takes a String and would return the bi-consumer:

Function<String, BiConsumer<Boolean,Integer>> f = s -> (b, i) -> System.out.println(s+b+i);
f.apply("foo").accept(true, 1);
Community
  • 1
  • 1
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • It is really wird to do so simple job I need to declare anything, why should I use lambda if I need to declare an interface, better to declare function. I thought that something similar (still clean) is possible: (copied from http://stackoverflow.com/questions/18400210/java-8-where-is-trifunction-and-kin-in-java-util-function-or-what-is-the-alt) `Function>> tri = a -> b -> c -> a + b + c; System.out.println(tri.apply(1).apply(2).apply(3));` exactly of function, biconsumer I thought - but this is a workaround but still nice, thank you – niedomnie Jun 30 '16 at 08:26
  • 1
    @niedomnie To use a lambda, there needs to be a functional interface defined. There are some built-in in the API but there can't be every combination (after Function3, why not Function4 and 5...) – Tunaki Jun 30 '16 at 08:30
3

Java has no real function types (like real functional languages). Instead, you need to define a functional interface. For functions and consumers with up to two parameters these functional interfaces have been defined in the standard library, but if you need three parameter you will have to define it yourself. Declaring that interface is the Java way to do it. But, looking at your comment to Tunaki's answer, you don't want to do that.

You can use a trick which is very common in real functional programming languages (but not in Java). It is called currying. A function with two parameters can be seen as a function which reads the first parameter and returns a function which reads the second parameter. The second function is the output of the first function.

In your example code however, the second BiConsumer is not output, it's input! And that means something completely different. You define a BiConsumer which receives a callback as input.

Instead, you need to define a function which provides the BiConsumer as output:

Function<String,BiConsumer<Boolean,Integer>> cc2;
cc2 = s -> (b,i) -> System.out.println(s + b + i); 

or two functions which provide a Consumer as output:

Function<String, Function<Boolean, Consumer<Integer>>> cc2;
cc2 = s -> b -> i -> System.out.println(s + b + i);

But if you ask me, this deviates too much from Java idiom. I would simply define that interface.

Hoopje
  • 12,677
  • 8
  • 34
  • 50