为什么匿名类不能访问其封闭类的变量?

Adr*_*rez 18 java closures class

我正在阅读java中的匿名类,它说你可以访问封闭类的方法,但不能访问局部变量.为什么会这样?我在说这个:

编辑:旧的例子是错误的不反映我的意思.这应该是一个更好的例子,根据它在"访问封闭类的成员"部分http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html中所写的内容.

public class MyClass {
    public interface SomeInterface{
        public void someOtherMethod();
    }

    public void someMethod(int someLocalVar) {
        SomeInterface myClass = new SomeInterface(){
            public void someOtherMethod(){
                someLocalVar = 0; // This must be final to work
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

那么这个限制解决有什么问题呢?

gna*_*nat 15

这来自早期版本的Java内部类规范.

官方规范URL,例如来自VM spec 2.14的链接腐烂消失:http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html

1999年1月17日快照可以在回程机器上获得,相应的规格部分是对局部变量的引用.

事物应该如何工作的方式描述如下(我标记了最相关的声明粗体):

块的本地类定义可以访问局部变量.这使编译器的工作变得复杂.这是本地类的前一个示例:

    Enumeration myEnumerate(final Object array[]) {
        class E implements Enumeration {
            int count = 0;
            public boolean hasMoreElements()
                { return count < array.length; }
            public Object nextElement() {
                { return array[count++]; }
        }
        return new E();
    }
Run Code Online (Sandbox Code Playgroud)

为了使局部变量对内部类的方法可见,编译器必须将变量的值复制到内部类可以访问它的位置.对同一变量的引用可以在不同的地方使用不同的代码序列,只要在任何地方产生相同的值,使得名称始终在其范围的所有部分中引用相同的变量.

按照惯例,将局部变量like array复制到val$array内部类的私有字段中.(由于arrayfinal,这样的副本不会包含值不一致.) ...

你知道,语言设计者希望每次创建这样的副本时,复制的局部变量的值都是"一致的".他们的动机很可能是开发人员不必担心在内部类的副本之外查看它是否已被更改:

Enumeration myEnumerate(Object array[], int copy) { // array not final, let's see...
    for (int i = 0, i < 2; i++ ) { // loop to have several copies
        class E implements Enumeration {
            int count = 0;
            public boolean hasMoreElements()
                { return count < array.length; }
            public Object nextElement() {
                { return array[count++]; }
        } // we hope to be done with E... oh no 

        array = null; // not final => can change

        if (i == copy) {
            return new E(); // we need to look outside of E
            // to figure value of array it uses
        }
    }
    return null;
}
Run Code Online (Sandbox Code Playgroud)

注意虽然spec示例使用命名类,但同样的推理适用于匿名类:

// ...
    for (int i = 0, i < 2; i++ ) { // loop to have several copies
        if (i == copy) {
            return new Enumeration() {
                int count = 0;
                public boolean hasMoreElements()
                    { return count < array.length; }
                public Object nextElement() {
                    { return array[count++]; }
            } // we hope to be done... oh no
        }

        array = null; // not final => can change
    }
Run Code Online (Sandbox Code Playgroud)

  • 您正在回答与OP的代码不匹配的问题.在OP的代码中,他显示了一个*实例变量*,它绝对可以*从内部类中访问和修改. (2认同)
  • @gnat 我误解了你所说的“可以改变”的意思。我以为你的意思是复制是为了不能更改引用的对象(例如设置数组中的元素),这当然仍然可能发生。 (2认同)

Mat*_*ick 11

内部类可以访问final封闭类的变量.


这是一个有趣的备忘​​录:

实际上,原型实现确实允许从内部类中引用非final变量.用户强烈抗议,抱怨说他们不想要这个!原因很有趣:为了支持这些变量,有必要对它们进行堆分配,并且(至少在那个时候),普通的Java程序员对堆分配和垃圾收集仍然相当不安.当没有出现"new"关键字时,他们不赞成在"桌面下"执行堆分配的语言.

  • @Nayuki:堆*分配*方面是一种伪装,因为捕获的“final”变量与捕获的可变变量占用的内存量完全相同。更重要的是,对变量的每次*访问*突然都是一次堆访问,这不是性能问题,而是线程安全问题。到目前为止,您可以安全地假设每个局部变量都是本质安全的,因为它是 *​​local*。必须分析整个代码以找出局部变量是否真的是局部变量,这是不可接受的。好吧,我们可以引入一个“shared”关键字或类似的…… (2认同)