Final field and anonymous class

妖精的绣舞 提交于 2019-12-04 05:33:31

It seems to me that you're getting confused between a variable being declared final, and it being a constant.

The compiler doesn't replace all references to local variables with a constant - but when the instance of the anonymous class is constructed, the current value of each relevant variable is passed to the constructor, and stored in a variable within the anonymous class. That's just as fine for a parameter as for any other kind of local variable.

So this code:

public static void method(final int x) {
    Runnable r = new Runnable() {
        @Override public void run() {
            System.out.println(x);
        }
    };
    r.run();
}

is broadly equivalent to:

public static void method(final int x) {
    Runnable r = new AnonymousRunnable(x);
    r.run();
}

private static class AnonymousRunnable implements Runnable {
    private final int x;

    AnonymousRunnable(int x) {
        this.x = x;
    }

    @Override public void run() {
        System.out.println(x);
    }
}

I've made both the method and the nested class static to avoid worrying about whether this is captured or not.

The local variable has to be final when it's captured to avoid situations which could otherwise be confusing. Suppose that weren't the case - consider this example:

void method() {
    int x = 10;
    Runnable r = new Runnable() {
        @Override public void run() {
            System.out.println(x);
        }
    };
    x = 20;
    r.run(); // Should this print 10 or 20?
}

Using the current way that anonymous classes work, but just removing the final restriction, it would print 10... but developers might expect it to print 20. Likewise, you should consider what would happen if you modified x within the run method. (As noted in Joop's answer, in Java 8 captured local variables are "effectively final" - so they act as if you've declared them to be final, but without doing so explicitly.)

As an example of a different approach, C# handles closures (for anonymous functions) in a different way, hoisting the local variables to a sort of anonymous class so that they can be modified. It's a more complex approach, but a bit more flexible. You might find my article about closures in Java and C# useful.

Because of the need for copying the variables from the method to the anonymous class (as described), it was a language design decision to require that the variable copied be final. So assignment in either method or anonymous class would not give stale values, and the code would be more consistent.

But! In Java 8 this requirement is mitigated: final no longer is needed, if the variable is de facto final: assignments are disallowed after the variable is "copied" in the anonymous class.

This makes sense because of the many function notations. Otherwise a button's actionPerformed suddenly would need its parameter to be final, when propagating it to another functional call.

I think you get confused because you used base types. If you think about references it should get more clear.

You are right at the creation an anonymous class copies all the references to its own context. And to be allowed to do that all the used local variables (and parameters are just another kind of local variables) have to be final. So it is not about the value, it is about the reference. And base types are a special case in java (that is sad). They treated like references in that case.

Hope this clarifies this final problem.

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