开发者

How do Inner blocks in Java access local variables which are supposed to be out-of-scope? (How the JVM treats final local variables in Java)

In the following code:

public class Main
{
    Emp globalEmp;

    public void aMethod()
    {
        final int stackVar = 10;

        globalEmp = new Emp()
        {
            public void doSomeThing()
            {
                System.out.println("stackVar :" + stackVar);
            }
        };
    }

    public static void main(String[] args)
    {
        Main m = new Main();
        m.aMethod();
        m.globalEmp.doSomeThing();
    }
}

interface Emp{
    public void doSomeThing();
}

As I can understand the following will be executed:

  1. Main m = new Main(); : A new Instance of the Main class will be created, with globalEmp set to null.

  2. m.aMethod(); : Invocation of aMethod which involves copying its instance variable stackVar into stack and then creating a new Instance of the Emp class and assign it to globalEmp instance.

  3. the local variable stackVar will be pushed out from the stack when the method aMethod reaches end.

  4. m.globalEmp.doSomeThing(); : The function doSomeThing will be called on the previously-created object pointed by the globalEmp variable. and since thi开发者_如何学编程s function doSomeThing is being access a local variable stackVar which supposed is not pop up from the cache, It should throw some error stating that.

So, How this really works by the Java runtime?

EDIT:

  1. Since the runtime will create a shallow copy of the final local variables (per answers below), why doesn't it allow access for non-final vars too?

  2. Can you provide me with some link taking about that (in the specs or some official place) ?


Usually, the anonymous inner class would have a constructor generated for it to take the values of the local variables which are referred to within the class of the anonymous inner class. The generated code turns this:

globalEmp = new Emp() { ... };

into:

globalEmp = new Main$1(stackVar);

That constructor then copies the value into hidden fields within the generated anonymous class.

In other words, it creates a copy of the value. The variable that the original value came from is irrelevant after that.

In the case you've actually shown, it wouldn't need to because 10 is a constant - but that's the general way that variables are captured.

EDIT: Responding to your edit...

Variables have to be final so that there's no possibility of confusion - if you could change them, some developers might expect any changes to be visible within the anonymous class, as they are in other closure implementations.

As for official documentation around this, the closest I can see in the Java Language Specification is in section 8.1.3, although it doesn't talk about the motivation behind the various decisions.


Anonymous inner classes (which is what new Emp() { } defines) will create a copy of any final local variables that they reference. Internally they will only ever access that copy and not the final variable itself.

This way the code in the anonymous inner class can "reference" the local variable long after the method invocation in which it existed is done.

That's also the reason why only final local variables can be accessed in anonymous inner classes: if you could do the same thing with non-final local variables, then this copying-trick would be very obvious (as you could never observe the changed value, only the initial one at the time of the copy being made).


It will be pushed out of scope for the method; but since the variable is final, the machine keeps a reference/copy to it for the Emp class created and stored in globalEmp.

Until aMethod is called again; the int value will stay in memory since it's still referenced in the Emp instance stored in the Main instance. After a second call, the Emp instance is removed together with the int after a garbage collect.

This is also the reason you have to make the variable final in order to use it in abstract inner classes; otherwise the referenced variable could change it's content (value) and thus break things. This can be done safely since a final object can only be read, and never modified :).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜