问题
final Object o;
List l = new ArrayList(){{
// closure over o, in lexical scope
this.add(o);
}};
why must o
be declared final? why don't other JVM languages with mutable vars have this requirement?
回答1:
This is not JVM-deep, it all happens at syntactic-sugar level. The reason is that exporting a non-final var via a closure makes it vulnerable to datarace issues and, since Java was designed to be a "blue-collar" language, such a surprising change in the behavior of an otherwise tame and safe local var was deemed way too "advanced".
回答2:
It's not hard to deduce logically why it has to be final
.
In Java, when a local variable is captured into an anonymous class, it is copied by value. The reason for this is that the object may live longer than the current function call (e.g. it may be returned, etc.), but local variables only live as long as the current function call. So it is not possible to simply "reference" the variable because it may not exist by then. Some languages like Python, Ruby, JavaScript, do allow you to reference variables after the scope is gone, by keeping a reference to the environment in the heap or something. But this is hard to do with the JVM because local variables are allocated on the function's stack frame, which is destroyed when the function call is done.
Now, since it is copied, there are two copies of the variable (and more, if there are more closures capturing this variable). If they were assignable, then you can change one of them without changing the other. For example, hypothetically:
Object o;
Object x = new Object(){
public String toString() {
return o.toString();
}
};
o = somethingElse;
System.out.println(x.toString()); // prints the original object, not the re-assigned one
// even though "o" now refers to the re-assigned one
Since there is only one o
variable in the scope, you would expect them to to refer to the same thing. In the example above, after you assign to o
, you would expect a later access of o
from the object to refer to the new value; but it doesn't. This would be surprising and unexpected to the programmer, and violates the principle that uses of the same variable refer to the same thing.
So to avoid this surprise, they mandate that you cannot assign to it anywhere; i.e. it has to be final
.
Now, of course, you can still initialize the final
variable from a non-final
variable. And inside the closure, you can still assign the final
variable to something else non-final
.
Object a; // non-final
final Object o = a;
Object x = new Object(){
Object m = o; // non-final
public String toString() {
return ,.toString();
}
};
But then this is all good since you are explicitly using different variables, so there is no surprise about what it does.
来源:https://stackoverflow.com/questions/10626132/in-java-why-do-closured-variables-need-to-be-declared-final