12

Im reading Java 8 in Action. In section 3.5.2 there is a paragraph about 'void-compatibility rule':

If a lambda has a statement expression as its body, it’s compatible with a function descriptor that returns void (provided the parameter list is compatible too). For example, both of the following lines are legal even though the method add of a List returns a boolean and not void as expected in the Consumer context (T -> void):

// Predicate has a boolean return 
Predicate<String> p = s -> list.add(s); 
// Consumer has a void return 
Consumer<String> b = s -> list.add(s);

How would you describe 'statement expression' in general? I thought it was either statement or expression. Also this void-compatibility rule is not 100% clear to me, can you think of any other examples?

Pshemo
  • 122,468
  • 25
  • 185
  • 269
jarosik
  • 4,136
  • 10
  • 36
  • 53
  • If it helps, an "expression statement" is a stand-alone statement followed by a semicolon and the returned value, if any, is ignored. Example `new Integer();` that is and stand-alone line of code. If expression statement is in a lambda expression body (not block body), and the function type of the target type returns void, then the returned value of the expression statement is ignored.Basically, the jsl says : a lambda of the form `() -> expr`, where `expr` is a statement expression, is interpreted as either `() -> { return expr; }` or `() -> { expr; }`, depending on the target type. – masinger Mar 12 '22 at 01:22

3 Answers3

17

The term “statement expression” or “expression statement” refers to expressions that are also allowed to be used as a statement. They are described in the Java Language Specification, §14.8. Expression Statements.

They include:

  • Method Invocations
  • Assignments
  • Increment/Decrement expressions
  • Class Instance Creation expressions

So other examples are:

Consumer<String> b = s -> counter++;
Function<String,Integer> f = s -> counter++;

or

Consumer<String> b = s -> new BigDecimal(s);
Function<String,BigDecimal> f = s -> new BigDecimal(s);

As a rule of thumb, a lambda expression of the form x -> expression is only legal for a Consumer (or void function type in general), if x -> { expression; } would be legal too.

Simeon Leyzerzon
  • 18,658
  • 9
  • 54
  • 82
Holger
  • 285,553
  • 42
  • 434
  • 765
1

The common structure of lambda expression is that:

(parameter) -> {body};

{body} with a single line statement return statement is optional e.g.

Predicate<String> p = s -> list.add(s);

When braces are used return statement becomes mandatory e.g.

Predicate<String> p = s -> { return list.add(s);};

Now, consider a Functional Interface like Consumer with an abstract method which doesn't return any value. It can accept a statement which returns any value because compiler ignores return statement for such abstract methods.

And hence below statements holds true:

Consumer<String> b = s -> list.add(s);

Equivalent to:

Consumer<String> consumerExample = s -> {list.add(s);};

Below line is incorrect:

Consumer<String> consumerExample = s -> {return list.add(s);};

Therefore, if a lambda has statement expression as its body, its compatible with a function descriptor that returns void (provided that parameter list is compatible too).

Abhishek
  • 49
  • 3
0

It is explained in the previous post clearly... I am trying in my way!!!

A function descriptor with explicit return value can be supplied for a lambda function descriptor which is do not return any value.

For Example, Predicate T -> vs Consumer T -> void lambda expression which takes an input and gives boolean as return, Predicate, can be supplied for Consumer.

In the following example, public boolean add(E e) {} returns boolean, but it can be used in the lambda for Consumer, which returns void.

    Predicate<String> p = s -> list.add(s);
    p.test("helloworld"); // this returns boolean 

    Consumer<String> consumerExample = s -> list.add(s);
    consumerExample.accept("Welcometokpm"); // this returns void
jegadeesh
  • 109
  • 2
  • 8