19

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

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

4

3 回答 3

17

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

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

1999 年 1 月 17 日的快照可以在 Wayback 机器上获得,相应的规范部分是References to local variables

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

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

    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();
    }

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

按照惯例,局部变量 likearray被复制到val$array内部类的私有字段中。(因为arrayis final,这样的副本永远不会包含不一致的值。) ...

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

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
    }
于 2013-07-16T20:11:55.760 回答
11

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


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

实际上,原型实现确实允许从内部类中引用非最终变量。用户强烈抗议,抱怨他们不想要这个!原因很有趣:为了支持这些变量,有必要对它们进行堆分配,而且(至少在那个时候)普通的 Java 程序员仍然对堆分配和垃圾收集以及所有这些东西很敏感。当看不到“new”关键字时,他们不赞成“在桌子下”执行堆分配的语言。

于 2013-07-16T20:29:08.957 回答
0

匿名类对象的生命周期可以比创建它的方法长,但不能比父对象的生命周期长。

考虑以下

public void runSomething() {
   int a = 5;
   new Thread(new Runnable() {
      public void run() {
         a = 10;
      }
   }
}

Runnable 要修改哪个“a”变量。无法更改该方法的本地方法,因为该方法不再在堆栈上。

于 2013-07-16T17:43:37.180 回答