I'm reading this fantastic article about Lambda Expressions and the following is uncleared to me:
Don't count on the compiler to catch all concurrent access errors. The prohibition against mutation holds only for local variables.
I'm not sure that self experimenting would cover all the cases so I'm searching for a well defined rules about:
Don't count on the compiler to catch all concurrent access errors. The prohibition against mutation holds only for local variables. If matchesis an instance or static variable of an enclosing class, then no error is reported, even though the result is just as undefined.
Update 1:
free variables - that is, the variables that are not parameters and not defined inside the code.
In simple words I can conclude that Free variables are all the variables that are not parameters of the Lambda Expression and are not defined inside the same Lambda Expression ?
This looks like complicated "words" on a simpler topic. The rules are pretty much the same as for anonymous classes.
For example the compiler catches this:
int x = 3;
Runnable r = () -> {
x = 6; // Local variable x defined in an enclosing scope must be final or effectively final
};
But at the same time it is perfectly legal to do this(from a compiler point of view):
final int x[] = { 0 };
Runnable r = () -> {
x[0] = 6;
};
The example that you provided and uses matches
:
List<Path> matches = new ArrayList<>();
List<Path> files = List.of();
for (Path p : files) {
new Thread(() -> {
if (1 == 1) {
matches.add(p);
}
}).start();
}
has the same problem. The compiler does not complain about you editing matches(because you are not changing the reference matches
- so it is effectively final
); but at the same time this can have undefined results
. This operation has side-effects
and is discouraged in general.
The undefined results would come from the fact that your matches
is not a thread-safe
collection obviously.
And your last point : Does the result of the mutation is undefined even when I use a synchroniziton algorithm?
. Of course not. With proper synchronization updating a variable outside
lambda(or a stream) will work - but are discouraged, mainly because there would be other ways to achieve that.
EDIT
OK, so free variables are those that are not defined within the lambda code itself or are not the parameters of the lambda itself.
In this case the answer to 1) would be: lambda expressions are de-sugared to methods and the rules for free-variables
are the same as for anonymous classes. This has been discussed numerous times, like here. This actually answers the second question as well - since the rules are the same. Obviously anything that is final or effectively final
can be mutated. For primitives - this means they can't be mutated; for objects you can't mutate the references (but can change the underlying data - as shown in my example). For the 3) - yes.
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