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


问题内容

我正在阅读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
            }
        }
    }
}

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


问题答案:

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

链接腐烂的官方规范URL(例如从VM spec
2.14中
引用)已消失:http
:
//java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html

不过,可以在Wayback机器上获得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();
    }

为了使局部变量对内部类的方法可见,编译器必须将变量的值复制到内部类可以访问它的位置。只要在各处都产生相同的值,对同一变量的引用就可以在不同的地方使用不同的代码序列,因此该名称在其作用域的所有部分中始终表示相同的变量。

按照约定,将局部变量(例如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;
}

请注意,尽管规范示例使用命名类,但相同的推理也适用于匿名类:

// ...
    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
    }