in java, why do closured variables need to be declared final?

梦想与她 提交于 2021-02-16 19:37:26

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!