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 } }); }
当前回答
匿名类是一个内部类,严格的规则适用于内部类(JLS 8.1.3):
任何在内部类中使用但未声明的局部变量、形式方法参数或异常处理程序参数必须声明为final。在内部类中使用但未声明的任何局部变量必须明确地在内部类的主体之前赋值。
我还没有在jls或jvm上找到一个原因或解释,但我们知道,编译器为每个内部类创建了一个单独的类文件,它必须确保,在这个类文件上声明的方法(在字节码级别上)至少可以访问局部变量的值。
(Jon有完整的答案-我保留这一个未删除,因为有人可能对JLS规则感兴趣)
其他回答
正如注释中提到的,其中一些在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) {
final int[] res = new int[1];
b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
res[0] = a * 5;
}
});
// But at this point handler is most likely not executed yet!
// How should we now res[0] is ready?
}
然而,由于同步问题,这个技巧不是很好。如果处理程序稍后被调用,你需要1)同步访问res,如果处理程序从不同的线程调用2)需要有某种标志或指示res被更新
但是,如果匿名类在同一个线程中立即被调用,这个技巧就可以工作。如:
// ...
final int[] res = new int[1];
Runnable r = new Runnable() { public void run() { res[0] = 123; } };
r.run();
System.out.println(res[0]);
// ...
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)
访问被限制为局部final变量的原因是,如果所有的局部变量都是可访问的,那么它们首先需要被复制到一个单独的部分,在那里内部类可以访问它们,并且维护可变局部变量的多个副本可能会导致数据不一致。而final变量是不可变的,因此任何数量的拷贝都不会对数据的一致性产生任何影响。
private void f(Button b, final int a[]) {
b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
a[0] = a[0] * 5;
}
});
}
推荐文章
- 如何格式化Joda-Time DateTime仅为mm/dd/yyyy?
- 如何在POM.xml中引用环境变量?
- 如何在android中复制一个文件?
- 将整数转换为字符串,以逗号表示千
- 接口方法的最终参数-有什么意义?
- Java中的@UniqueConstraint注释
- 如何在清洁模式下运行eclipse ?如果我们这样做会发生什么?
- 获取java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory异常
- Java中的正则表达式命名组
- c#和Java的主要区别是什么?
- 什么是NullPointerException,我如何修复它?
- 在Java中使用“final”修饰符
- 无法在Flutter上找到捆绑的Java版本
- 如何在Kotlin解析JSON ?
- 如何在新的材质主题中改变背面箭头的颜色?