I cannot do this in Java:
Optional<String> optStr = Optional.of("foo");
String result;
optStr.ifPresent(s -> result = s);
The doc says that the variables used in lambdas must be effectively final. Then how to extract and store something from a lambda into a variable nicely?
Actually the real use case is more complex.
I want to apply several regular expressions to a string one after another with matcher.replaceAll
. I'm doing this in a forEach lambda and wanted to store intermediate results somewhere.
The answer is simple: you can't (directly) do that.
A very ugly hack would be to mutate an external object instead of assigning to a variable:
This works because
sb
is effectively final hereBut I want to emphasize the point that this is probably not what you really want to do.
optStr.orElse
oroptStr.orElseGet
instead.optStr.map
Since you did not provide your whole use-case, these are just guesses but the key point is that I really do not recommend the above snippet of code: it goes against the concept of functional programming (by mutating an object).
Another way, similar to what Tunaki has written, is to use a single-cell table:
The table object is final, what changes is its content.
Edit: A word of warning though - before using this hacky solution check out the other answers to OP's question, pointing out why it's a bad idea to use this workaround and consider if it's really worth it!
If the code
was legal, it still was useless, as the variable
result
is not definitely assigned after the invocation ofifPresent
. Java does not allow reading local variables which are only conditionally initialized. So you would need an alternative value forresult
for the case of an emptyOptional
, e.g.:But then, if you have defined such a default/fall-back value, you can use the method intended for this purpose:
When you say, you have to initialize more than one variable, it doesn’t change the fact that these variables need to be initialized in either case. Also, there is no benefit in performing the initialization of dependent variables inside a lambda expression passed to the
Optional
as, after all, theOptional
carries only a single value. Everything dependent on that value can get determined after getting that value, independently of theOptional
:Note that even if your variables are already pre-initialized and you want to mutate them, using
if(optional.isPresent()) /* local modifications */
is the simplest way to do it. There’s nothing that works better when you replace this idiom with a lambda expression.As per Java Specification, lambda gets the variable values as final from the surrounding context as the lambda is passed to that context at runtime.
The designer of the piece of code which accepts that lambda expression(or Functional Interface) accepts that interface instance\lambda with the faith that its values will not be altered. To strictly make the lambda faithful in this way Java language specification has kept this condition of accessing local context variables as final.
In short, a lambda is NOT supposed to change the state of the context in which it is invoked.
You can use an array to capture values, but still it is not advisable and hopefully java will detect and show warnings for such code in future revisions.