a can only be final here. Why? How can I reassign a in onClick() method without keeping it as private member? private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; } }); } How can I return the 5 * a when it clicked? I mean, private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; return b; // but return type is void } }); }


当前回答

您可以创建一个类级别的变量来获取返回值。我的意思是

class A {
    int k = 0;
    private void f(Button b, int a){
        b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            k = a * 5;
        }
    });
}

现在你可以得到K的值,并在你想要的地方使用它。

你的答案是:

局部内部类实例绑定到Main类,可以访问其包含方法的最终局部变量。当实例使用其包含方法的final局部时,即使变量超出了作用域(这实际上是Java中粗糙的、有限版本的闭包),变量也会保留它在创建实例时所持有的值。

因为局部内部类既不是类的成员也不是包的成员,所以没有使用访问级别声明它。(但是要清楚的是,它自己的成员像普通类一样具有访问级别。)

其他回答

正如注释中提到的,其中一些在Java 8中变得无关紧要,在Java 8中final可以是隐式的。但是,只有有效的final变量才能用于匿名内部类或lambda表达式。


这主要是由于Java管理闭包的方式。

When you create an instance of an anonymous inner class, any variables which are used within that class have their values copied in via the autogenerated constructor. This avoids the compiler having to autogenerate various extra types to hold the logical state of the "local variables", as for example the C# compiler does... (When C# captures a variable in an anonymous function, it really captures the variable - the closure can update the variable in a way which is seen by the main body of the method, and vice versa.)

As the value has been copied into the instance of the anonymous inner class, it would look odd if the variable could be modified by the rest of the method - you could have code which appeared to be working with an out-of-date variable (because that's effectively what would be happening... you'd be working with a copy taken at a different time). Likewise if you could make changes within the anonymous inner class, developers might expect those changes to be visible within the body of the enclosing method.

Making the variable final removes all these possibilities - as the value can't be changed at all, you don't need to worry about whether such changes will be visible. The only ways to allow the method and the anonymous inner class see each other's changes is to use a mutable type of some description. This could be the enclosing class itself, an array, a mutable wrapper type... anything like that. Basically it's a bit like communicating between one method and another: changes made to the parameters of one method aren't seen by its caller, but changes made to the objects referred to by the parameters are seen.

如果您对Java和c#闭包之间更详细的比较感兴趣,我有一篇文章对此进行了进一步的讨论。在这个答案中,我想专注于Java方面:)

private void f(Button b, final int a[]) {

    b.addClickHandler(new ClickHandler() {

        @Override
        public void onClick(ClickEvent event) {
            a[0] = a[0] * 5;

        }
    });
}

When an anonymous inner class is defined within the body of a method, all variables declared final in the scope of that method are accessible from within the inner class. For scalar values, once it has been assigned, the value of the final variable cannot change. For object values, the reference cannot change. This allows the Java compiler to "capture" the value of the variable at run-time and store a copy as a field in the inner class. Once the outer method has terminated and its stack frame has been removed, the original variable is gone but the inner class's private copy persists in the class's own memory.

(http://en.wikipedia.org/wiki/Final_%28Java%29)

匿名类是一个内部类,严格的规则适用于内部类(JLS 8.1.3):

任何在内部类中使用但未声明的局部变量、形式方法参数或异常处理程序参数必须声明为final。在内部类中使用但未声明的任何局部变量必须明确地在内部类的主体之前赋值。

我还没有在jls或jvm上找到一个原因或解释,但我们知道,编译器为每个内部类创建了一个单独的类文件,它必须确保,在这个类文件上声明的方法(在字节码级别上)至少可以访问局部变量的值。

(Jon有完整的答案-我保留这一个未删除,因为有人可能对JLS规则感兴趣)

您可以创建一个类级别的变量来获取返回值。我的意思是

class A {
    int k = 0;
    private void f(Button b, int a){
        b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            k = a * 5;
        }
    });
}

现在你可以得到K的值,并在你想要的地方使用它。

你的答案是:

局部内部类实例绑定到Main类,可以访问其包含方法的最终局部变量。当实例使用其包含方法的final局部时,即使变量超出了作用域(这实际上是Java中粗糙的、有限版本的闭包),变量也会保留它在创建实例时所持有的值。

因为局部内部类既不是类的成员也不是包的成员,所以没有使用访问级别声明它。(但是要清楚的是,它自己的成员像普通类一样具有访问级别。)