我不明白为什么我不能总是从'listener'或'handler'中访问变量。
这是我的代码:
Button btnDownload = new Button(myparent, SWT.NONE);
btnDownload.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
btnDownload.setEnabled(false); // I CAN'T
}
});
唯一的方法是使用final
关键字声明它:
final Button btnDownload = new Button(myparent, SWT.NONE);
为什么我需要声明变量 final 以获取事件内的访问权限?
答案 0 :(得分:38)
你的SelectionAdapter
是一个匿名的内部课程,我认为这说明了这一点:
本地类可以绝对引用实例变量。他们无法引用非最终局部变量的原因是因为本地类实例可以在方法返回后保留在内存中。当方法返回时,局部变量超出范围,因此需要它们的副本。如果变量不是最终的,那么方法中变量的副本可能会改变,而本地类中的副本则没有,所以它们会不同步。
匿名内部类需要最终变量,因为它们在Java中的实现方式。匿名内部类(AIC)通过创建一个私有实例字段来使用局部变量,该字段包含局部变量值的副本。内部类实际上并不使用局部变量,而是使用副本。在这一点上应该相当明显的是,如果原始值或复制值发生变化,可能会发生“Bad Thing”™;会有一些意外的数据同步问题。为了防止出现这种问题,Java要求您将AIC使用的局部变量标记为final(即不可更改)。这可以保证内部类的局部变量副本始终与实际值匹配。
答案 1 :(得分:17)
我相信Tom说的是,如果您可以在匿名类中使用局部变量,那么它应该在以下代码段中启用哪个按钮。
Button btnDownload = new Button(myparent, SWT.NONE);
btnDownload.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
btnDownload.setEnabled(false); // I CAN'T
}
});
btnDownload = new Button(somethingelse , SWT.NONE);
btnDownload = null ;
java的制作者希望通过要求匿名类中使用的局部变量是最终的来避免这种争论。
答案 2 :(得分:3)
这可能不是最佳设计选择。在Java 8中,随着lambda表达式,final
的这个要求有望被删除。
目标是禁止从匿名类分配本地变量。但这并不要求将局部变量标记为final。
void f()
Object var = ...;
new Anon(){
...
print(var); // reading is ok
var = x; // error, can't write to var [1]
}
编译器实际上创建了var的副本并将其保存在匿名类中。匿名类仅在以后访问该副本。上面的代码实际上转换为
void f()
Object var = ...;
new Anon$1(var);
class Anon$1
{
final Object $var;
Anon$1(Object var){ this.$var=var; }
...
print($var);
$var = x; // error, naturally [2]
}
正如您所看到的,没有技术理由要求var
是最终的。所有编译器在遇到[2]时都必须知道$var
是var
的合成字段,报告错误“本地变量var不能由匿名类分配”(而不是“ $ var是final,不能分配给“)
语言设计者选择通过在局部变量上使用final
关键字来骚扰我们;我不记得理由;一般来说,如果需要清晰度,Java并不害怕冗长。
答案 3 :(得分:0)
这篇文章解释了为什么