Why can't an anonymous class access variables

2019-01-22 20:16发布

This question already has an answer here:

I'm reading about anonymous classes in java and it says you can access the methods of the enclosing class, but not the local variables. Why is it like this? I'm talking about this:

EDIT: The older example was incorrect not reflecting what I meant. This should be a better example according with what its being written here in the section "Accessing members of enclosing class" 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
            }
        }
    }
}

So what problem is this restriction solving?

3条回答
冷血范
2楼-- · 2019-01-22 20:27

An anonymous class object can have a lifetime that lasts longer than the method that created it, but it cannot last longer than the lifetime of the parent object.

Consider the following

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

Which 'a' variable is the Runnable going to modify. Cannot change the one local to the method, because the method is no longer on the stack.

查看更多
迷人小祖宗
3楼-- · 2019-01-22 20:33

Inner classes can access final variables of enclosing classes.


Here's an interesting memo:

Actually, the prototype implementation did allow non-final variables to be referenced from within inner classes. There was an outcry from users, complaining that they did not want this! The reason was interesting: in order to support such variables, it was necessary to heap-allocate them, and (at that time, at least) the average Java programmer was still pretty skittish about heap allocation and garbage collection and all that. They disapproved of the language performing heap allocation "under the table" when there was no occurrence of the "new" keyword in sight.

查看更多
欢心
4楼-- · 2019-01-22 20:51

This comes from early version Java Inner Classes Specification.

Official specification URL, referred eg from VM spec 2.14 is gone for link rot: http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html

January 17, 1999 snapshot can be obtained at wayback machine though, respective spec section is References to local variables.

The way how things are supposed to work, is described as follows (I marked most relevant statement bold):

A class definition which is local to a block may access local variables. This complicates the compiler's job. Here is the previous example of a local class:

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

In order to make a local variable visible to a method of the inner class, the compiler must copy the variable's value into a place where the inner class can access it. References to the same variable may use different code sequences in different places, as long as the same value is produced everywhere, so that the name consistently appears to refer to the same variable in all parts of its scope.

By convention, a local variable like array is copied into a private field val$array of the inner class. (Because array is final, such copies never contain inconsistent values.) ...

You see, language designers wanted value of copied local variable to be "consistent" every time such a copy is created. Their motivation was most likely that developers would need not worry to look outside of the copy of inner class to check whether it has been changed out there:

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

Note although spec example uses named class, same reasoning applies to anonymous classes:

// ...
    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
    }
查看更多
登录 后发表回答