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

时间:2013-07-16 17:20:24

标签: java class closures

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

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

3 个答案:

答案 0 :(得分:14)

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

官方规范网址(例如来自VM spec 2.14)已被删除链接腐烂:http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html

1999年1月17日快照可以在返回机器上获得,相应的规格部分是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();
    }
     

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

     

按照惯例,像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;
}

注意虽然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
    }

答案 1 :(得分:11)

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


Here's一个有趣的备忘​​录:

  

实际上,原型实现确实允许非最终版   要在内部类中引用的变量。有   来自用户的强烈抗议,抱怨说他们不想要这个!   原因很有趣:为了支持这些变量,   有必要对它们进行堆分配,并且(至少在那时)   普通的Java程序员对堆还是很怯懦   分配和垃圾收集等等。他们不赞成   语言执行堆分配“在表下”时   看不到“新”关键字的出现。

答案 2 :(得分:0)

匿名类对象的生命周期可以比创建它的方法持续更长时间,但它的持续时间不能超过父对象的生命周期。

考虑以下

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

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