In a lambda, local variables need to be final, but instance variables don't. Why so?
相关问题
- Delete Messages from a Topic in Apache Kafka
- Jackson Deserialization not calling deserialize on
- How to maintain order of key-value in DataFrame sa
- StackExchange API - Deserialize Date in JSON Respo
- Difference between Types.INTEGER and Types.NULL in
Here is a code example, as I didn't expect this either, I expected to be unable to modify anything outside my lambda
Output: Odd=true
Putting up some concepts for future visitors:
Basically it all boils down to the point that compiler should be able to deterministically tell that lambda expression body is not working on a stale copy of the variables.
In case of local variables, compiler has no way to be sure that lambda expression body is not working on a stale copy of the variable unless that variable is final or effectively final, so local variables should be either final or effectively final.
Now, in case of instance fields, when you access an instance field inside the lambda expression then compiler will append a
this
to that variable access (if you have not done it explicitly) and sincethis
is effectively final so compiler is sure that lambda expression body will always have the latest copy of the variable (please note that multi-threading is out of scope right now for this discussion). So, in case instance fields, compiler can tell that lambda body has latest copy of instance variable so instance variables need not to be final or effectively final. Please refer below screen shot from an Oracle slide:Also, please note that if you are accessing an instance field in lambda expression and that is getting executed in multi-threaded environment then you could potentially run in problem.
Within Lambda expressions you can use effectively final variables from the surrounding scope. Effectively means that it is not mandatory to declare variable final but make sure you do not change its state within the lambda expresssion.
You can also use this within closures and using "this" means the enclosing object but not the lambda itself as closures are anonymous functions and they do not have class associated with them.
So when you use any field (let say private Integer i;)from the enclosing class which is not declared final and not effectively final it will still work as the compiler makes the trick on your behalf and insert "this" (this.i).
YES, you can change the member variables of the instance but you CANNOT change the instance itself just like when you handle variables.
Something like this as mentioned:
The basic principle to restrict the local variables is about data and computation validity
But as we know the principal purpose of the lambdas
Quite unlike local variables, local instance can be mutated, because it's shared globally. We can understand this better via the heap and stack difference:
So to sum up, there are two points I think really matter:
It's really hard to make the instance effectively final, which might cause lots of senseless burden (just imagine the deep-nested class);
the instance itself is already globally shared and lambda is also shareable among threads, so they can work together properly since we know we're handling the mutation and want to pass this mutation around;
Balance point here is clear: if you know what you are doing, you can do it easily but if not then the default restriction will help to avoid insidious bugs.
P.S. If the synchronization required in instance mutation, you can use directly the stream reduction methods or if there is dependency issue in instance mutation, you still can use
thenApply
orthenCompose
in Function whilemapping
or methods similar.It seems like you are asking about variables that you can reference from a lambda body.
From the JLS §15.27.2
So you don't need to declare variables as
final
you just need to make sure that they are "effectively final". This is the same rule as applies to anonymous classes.Because instance variables are always accessed through a field access operation on a reference to some object, i.e.
some_expression.instance_variable
. Even when you don't explicitly access it through dot notation, likeinstance_variable
, it is implicitly treated asthis.instance_variable
(or if you're in an inner class accessing an outer class's instance variable,OuterClass.this.instance_variable
, which is under the hoodthis.<hidden reference to outer this>.instance_variable
).Thus an instance variable is never directly accessed, and the real "variable" you're directly accessing is
this
(which is "effectively final" since it is not assignable), or a variable at the beginning of some other expression.