匿名内部类中的 Java 局部变量可见性 - 为什么需要“final”关键字?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7423028/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 19:54:40  来源:igfitidea点击:

Java local variable visibility in anonymous inner classes - why is 'final' keyword required?

javascope

提问by stighy

I don't understand why I cannot always access a variable from inside a 'listener' or 'handler'.

我不明白为什么我不能总是从“侦听器”或“处理程序”内部访问变量。

This is my code:

这是我的代码:

Button btnDownload = new Button(myparent, SWT.NONE);
btnDownload.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                btnDownload.setEnabled(false); // I CAN'T 
            }
        });

The only way is to declare it using the finalkeyword:

唯一的方法是使用final关键字声明它:

final Button btnDownload = new Button(myparent, SWT.NONE);

Why do I need to declare a variable finalto gain access inside an event?

为什么我需要声明一个变量final才能在事件中访问?

回答by user219882

Your SelectionAdapteris an anonymous inner class and I think this makes it clear:

SelectionAdapter是一个匿名的内部类,我认为这很清楚:

Local classes can most definitely reference instance variables. The reason they cannot reference non final local variables is because the local class instance can remain in memory after the method returns. When the method returns the local variables go out of scope, so a copy of them is needed. If the variables weren't final then the copy of the variable in the method could change, while the copy in the local class didn't, so they'd be out of synch.

本地类绝对可以引用实例变量。它们不能引用非 final 局部变量的原因是因为局部类实例可以在方法返回后保留在内存中。当方法返回时,局部变量超出范围,因此需要它们的副本。如果变量不是最终的,则方法中变量的副本可能会更改,而本地类中的副本则不会,因此它们将不同步。

Anonymous inner classes require final variables because of the way they are implemented in Java. An anonymous inner class (AIC) uses local variables by creating a private instance field which holds a copy of the value of the local variable. The inner class isn't actually using the local variable, but a copy. It should be fairly obvious at this point that a “Bad Thing”? can happen if either the original value or the copied value changes; there will be some unexpected data synchronization problems. In order to prevent this kind of problem, Java requires you to mark local variables that will be used by the AIC as final (i.e., unchangeable). This guarantees that the inner class' copies of local variables will always match the actual values.

匿名内部类需要最终变量,因为它们在 Java 中的实现方式。匿名内部类 (AIC) 通过创建一个私有实例字段来使用局部变量,该字段包含局部变量值的副本。内部类实际上并没有使用局部变量,而是一个副本。在这一点上应该是相当明显的“坏事”?如果原始值或复制的值发生变化,就会发生这种情况;会有一些意想不到的数据同步问题。为了防止此类问题,Java 要求您将 AIC 将使用的局部变量标记为 final(即不可更改)。这保证了内部类的局部变量副本将始终与实际值匹配。

回答by emory

I believe that Tomis saying is that if you could use local variables in an anonymous class then which button should it enable in the following code segment.

我相信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 ;

The makers of java wanted to avoid that debate by requiring local variables used in anonymous classes to be final.

java 的创造者希望通过要求匿名类中使用的局部变量是最终的来避免这种争论。

回答by irreputable

It's probably not the optimum design choice. In Java 8, along with lambda expression, this requirement of finalhopefully will be dropped.

这可能不是最佳设计选择。在 Java 8 中,随着 lambda 表达式的出现,这个有final希望的要求将被删除。

The goal is to forbid assigning to the local variable from the anonymous class. But this doesn't require marking the local variable as final.

目标是禁止从匿名类分配给局部变量。但这不需要将局部变量标记为 final。

void f()

  Object var = ...;
  new Anon(){
      ...
      print(var); // reading is ok
      var = x;    // error, can't write to var            [1]
  }

The compiler actually makes a copy of the var and save it in the anonymous class. The anonymous class only accesses the copy afterwards. The code above is actually transformed to

编译器实际上制作了 var 的副本并将其保存在匿名类中。匿名类只在之后访问副本。上面的代码实际上转化为

void f()

  Object var = ...;
  new Anon(var);

class Anon
{
    final Object $var;
    Anon(Object var){ this.$var=var; }
    ...
    print($var); 
    $var = x;    // error, naturally                      [2]
}

As you can see, there is no technical reason to require that varis final. All compiler has to do, when it encounters [2], knowing that $varis a synthesized field for var, report error "local variable var cannot be assigned to by the anonymous class" (instead of "$var is final and cannot be assigned to")

如您所见,没有技术理由要求它var是最终的。所有编译器要做的,当遇到[2]时,知道$var是for的合成字段var,报错“局部变量var不能被匿名类赋值”(而不是“$var is final and cannot beassigned to” )

The language designers chose to annoy us by requiring the finalkeyword on the local variable; I don't remember the rationale; in general Java wasn't afraid of verbosity if clarity is desired.

语言设计者选择final在局部变量上要求关键字来惹恼我们;我不记得理由了;一般来说,如果需要清晰,Java 并不害怕冗长。

回答by Uri Lukach

This post explains why

这篇文章解释了为什么

Java Method Variables & Anonymous Class

Java 方法变量和匿名类