为什么 Java 内部类需要“最终”外部实例变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3910324/
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-08-14 06:42:31  来源:igfitidea点击:

Why Java inner classes require "final" outer instance variables?

javainner-classes

提问by 3m masr

final JTextField jtfContent = new JTextField();
btnOK.addActionListener(new java.awt.event.ActionListener(){
    public void actionPerformed(java.awt.event.ActionEvent event){
        jtfContent.setText("I am OK");
    }
} );

If I omit final, I see the error "Cannot refer to a non-final variable jtfContent inside an inner class defined in a different method".

如果我省略final,我会看到错误“无法在不同方法中定义的内部类中引用非最终变量 jtfContent”。

Why must an anonymous inner class require the outer classes instance variable to be final in order to access it?

为什么匿名内部类必须要求外部类实例变量是 final 才能访问它?

采纳答案by Pointy

Well first, let's all relax, and please put that gun down.

那么首先,大家都放松一下,请把枪放下。

OK. Now the reason the language insists on that is that it cheats in order to provide your inner class functions access to the local variables they crave. The runtime makes a copy of the local execution context (and etc. as appropriate), and thus it insists that you make everything finalso it can keep things honest.

好的。现在,该语言坚持这一点的原因是为了让您的内部类函数访问它们渴望的局部变量,它会作弊。运行时会复制本地执行上下文(以及其他适当的内容),因此它坚持让您制作所有内容,final以便保持诚实。

If it didn't do that, then code that changed the value of a local variable after your object was constructed but beforethe inner class function runs might be confusing and weird.

如果它没有这样做,那么在构造对象之后但内部类函数运行之前更改局部变量值的代码可能会令人困惑和奇怪。

This is the essence of a lot of the brouhaha around Java and "closures".

这是许多围绕 Java 和“闭包”的喧嚣的本质。



Note: the opening paragraph was a joke in reference to some all-caps text in the original composition of the OP.

注意:开头段落是一个笑话,引用了 OP 原始作文中的一些全大写文本。

回答by Jorge Ferreira

The methods in an anonymous class don't really have access to local variables and method parameters. Rather, when an object of the anonymous class is instantiated, copies of the finallocal variables and method parameters referred to by the object's methods are stored as instance variables in the object. The methods in the object of the anonymous class really access those hidden instance variables. [1]

匿名类中的方法并不能真正访问局部变量和方法参数。相反,当匿名类的对象被实例化时,对象方法引用的最终局部变量和方法参数的副本作为实例变量存储在对象中。匿名类对象中的方法真正访问那些隐藏的实例变量。[1]

Thus, the local variables and method parameters accessed by the methods of the local class must be declared final to prevent their values from changing after the object is instantiated.

因此,本地类的方法访问的本地变量和方法参数必须声明为final,以防止在对象实例化后它们的值发生变化。

[1] http://www.developer.com/java/other/article.php/3300881/The-Essence-of-OOP-using-Java-Anonymous-Classes.htm

[1] http://www.developer.com/java/other/article.php/3300881/The-Essence-of-OOP-using-Java-Anonymous-Classes.htm

回答by Bart van Heukelom

The variables around the definition of the class live on the stack, so they are probably gone when the code inside the inner class runs (if you want to know why, search stack and heap). That's why inner classes don't actually use the variables in the containing method, but are constructed with copies of them.

类定义周围的变量存在于堆栈中,因此当内部类中的代码运行时,它们可能会消失(如果您想知道原因,请搜索堆栈和堆)。这就是为什么内部类实际上并不使用包含方法中的变量,而是用它们的副本构造。

This means that if you change the variable in the containing method after constructing the inner class, its value won't change in the inner class, even though you'd expect it to. To prevent confusion there, Java requires them to be final, so you expect not to be able to modify them.

这意味着如果您在构造内部类后更改包含方法中的变量,则其值不会在内部类中更改,即使您期望它会更改。为防止出现混淆,Java 要求它们是最终的,因此您预计无法修改它们。

回答by Thorbj?rn Ravn Andersen

The reason is that Java do not fully support so-called "Closures" - in which case the finalwould not be necessary - but instead have found a trick by letting the compiler generate some hidden variables which is used to give the functionality you see.

原因是 Java 不完全支持所谓的“闭包”——在这种情况下final不需要——而是通过让编译器生成一些隐藏变量来找到一个技巧,这些变量用于提供你看到的功能。

If you disassemble the generated byte code you can see how the compiler does it, including the strangely named hidden variables containing copies of the final variables.

如果你反汇编生成的字节码,你可以看到编译器是如何做的,包括包含最终变量副本的奇怪命名的隐藏变量。

It is an elegant solution to give functionality without bending the language backwards to do so.

这是一个优雅的解决方案,可以在不向后弯曲语言的情况下提供功能。



Edit: For Java 8 lambdas give a more concise way to do what was previously done with anonymous classes. The restrictions on variables have also loosened from "final" to "essentially final" - you do not have to declare it final, but if it is treatedlike it is final (you could add the final keyword and your code would still compile) it can be used. This is a really nice change.

编辑:对于 Java 8 lambdas 提供了一种更简洁的方法来完成以前使用匿名类所做的事情。对变量的限制也从“最终”放宽到“本质上是最终的”——您不必将其声明为最终的,但如果将其视为最终的(您可以添加 final 关键字并且您的代码仍会编译)它可以使用。这是一个非常好的改变。

回答by anstarovoyt

Since Java 8 finalmodifier is optional for outer instance variables. Value should be 'effectively final'. See answer Difference between final and effectively final.

由于 Java 8 final修饰符对于外部实例变量是可选的。值应该是“有效最终的”。请参阅答案final 和 Effective final 之间的区别