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 } }); }
当前回答
正如注释中提到的,其中一些在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方面:)
其他回答
匿名内部类中的方法可以在生成该类的线程终止后调用。在您的示例中,内部类将在事件分派线程上调用,而不是在创建它的线程中调用。因此,变量的作用域是不同的。所以为了保护这样的变量赋值范围问题,你必须声明它们为final。
试试这段代码,
创建数组列表并将值放入其中并返回:
private ArrayList f(Button b, final int a)
{
final ArrayList al = new ArrayList();
b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
int b = a*5;
al.add(b);
}
});
return al;
}
Java内部类中的final变量[关于]
内部类只能使用
外部类引用 final局部变量来自范围外的引用类型(例如Object…) 值(原语)(例如int…)类型可以由最终引用类型包装。IntelliJ IDEA可以帮助您将其转换为一个元素数组
当编译器生成一个非静态嵌套(内部类)时-一个新类- <OuterClass>$<InnerClass>.class创建并将有界参数传递给构造函数[堆栈上的局部变量],它类似于闭包[Swift about]
Final变量是不能重新赋值的变量。最终引用变量仍然可以通过修改状态来更改
如果这是可能的,那就奇怪了,因为作为一个程序员,你可以做出这样的东西
//Not possible
private void foo() {
MyClass myClass = new MyClass(); //Case 1: myClass address is 1
int a = 5; //Case 2: a = 5
//just as an example
new Button().addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
/*
myClass.something(); //<- what is the address - 1 or 2?
int b = a; //<- what is the value - 5 or 10 ?
//illusion that next changes are visible for Outer class
myClass = new MyClass();
a = 15;
*/
}
});
myClass = new MyClass(); //Case 1: myClass address is 2
int a = 10; //Case 2: a = 10
}
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)
由于Jon有实现细节的答案,另一个可能的答案是JVM不想处理已经结束他的激活的写入记录。
考虑这样一个用例,在这个用例中,你的lambdas不是被应用的,而是被存储在某个地方并稍后运行。
我记得在Smalltalk中,当你做这样的修改时,你会得到一个非法的商店。
推荐文章
- Java 8接口方法中不允许“同步”的原因是什么?
- 如何找到Java堆大小和内存使用(Linux)?
- 使用Enum实现单例(Java)
- RabbitMQ与通道和连接之间的关系
- buildSessionFactory()配置方法在Hibernate中已弃用?
- Spring MVC -如何获得所有的请求参数在一个地图在Spring控制器?
- 如何在Java中按两个字段排序?
- 文件之间的差异。路径中的分隔符和斜杠
- 在方法参数中使用NotNull注释
- Spring MVC中处理可选参数的@RequestParam
- Tomcat:如何查找正在运行的Tomcat版本?
- “java”、“javaw”和“javaws”之间有什么区别?
- 将Date对象转换为日历对象
- 在Java中保存最后N个元素的大小有限的队列
- 如何运行一个类从Jar不是主类在其清单文件