I possibly use the wrong terms, feel free to correct.
I have a test method which takes a Runnable
:
void expectRollback(Runnable r) { .. }
I can call this method like this:
expectRollback(() -> aList.add(x))
Cool, I understand lambdas! This is awesome. Let's be super clever...
expectRollback(() -> aList.add(x) && bList.add(y))
But what? That doesn't compile: 'void' methods cannot return a value.
Doesn't the first call also return a value though? What is the difference between the first and the second call?
The lambda expressions have a very simple, precise syntax and provide flexibility to specify the datatypes for the function parameters. Its return type is a parameter -> expression body to understand the syntax, we can divide it into three parts.
Statement lambdasThe body of a statement lambda can consist of any number of statements; however, in practice there are typically no more than two or three.
A lambda expression used with any functional interface and Comparator is a functional interface. The Comparator interface has used when sorting a collection of objects compared with each other. In the below example, we can sort the employee list by name using the Comparator interface.
It's subtle, but I think I've got it.
In JLS 15.27.3 we have:
A lambda expression is congruent with a function type if all of the following are true:
- ...
- If the lambda parameters are assumed to have the same types as the function type's parameter types, then:
- If the function type's result is
void
, the lambda body is either a statement expression (§14.8) or a void-compatible block.- ...
- ...
Now aList.add(x)
is a statement expression, but aList.add(x) && bList.add(y)
is not. You can see that without any lambdas involved - just try to use them as statements:
aList.add(x); // Fine
aList.add(x) && bList.add(y); // Error: "not a statement"
If you wrap that expression in a method call, it's fine, because the method call is a statement expression again:
rollback(() -> Boolean.valueOf(aList.add(x) && bList.add(y)));
I'm not actually suggesting you do that - just trying to explain why that would work but your previous attempt didn't.
If we assume that List.add
really will return true
every time as documented, just use a block lambda:
rollback(() -> { aList.add(x); bList.add(y); });
I'd argue that's clearer anyway as it makes it more obvious that you don't care about the value returned by the first add
call.
Basically your first expression is way to invoke a method but ignore the result:
aList.add(x) // actually returns a boolean but you ignore this result
This is not anything new for lambdas actually, as it has been like this since java appeared:
List<Integer> list ...
list.add(1); // returns boolean but we ignore the result almost always
So why is the result not ignored in your second example? Well because the JLS
says so, as seen here. There are 4 types that will work though:
Method Invocations
Assignments
Increment and Decrement expressions
Class Instance Creation expressions
Your last example uses &&
which is neither of the 4 types described in the JLS, thus a compile time error.
You could create a helper
method to add to both lists(or as you did with Boolean.valueOf
):
public static boolean helper(int x) {
boolean result = first.add(x) && second.add(x);
return result;
}
And use it:
expectRollback(() -> helper(x))
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