Почему не может быть анонимным классом доступ к переменным его охватывающего класса?

Я читаю об анонимных классах в java, и он говорит, что вы можете обращаться к методам охватывающего класса, но не к локальным переменным. Почему так? Я говорю об этом:

EDIT: старый пример был неправильным, не отражая, что я имел в виду. Это должно быть лучшим примером в соответствии с тем, что оно написано здесь в разделе "Доступ к членам охватывающего класса" 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
            }
        }
    }
}

Итак, какова проблема этого решения?

Ответ 1

Это связано с ранней версией Java Inner Classes.

Официальный URL-адрес спецификации, например, от Спецификация VM 2.14 исчезла для ссылки rot: http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html

17 января 1999 года моментальный снимок можно получить на машине обратного пути, но соответствующий раздел спецификации Ссылки на локальные переменные.

Способ работы должен описываться следующим образом (я обозначил наиболее актуальную формулировку жирным шрифтом):

Определение класса, которое является локальным для блока, может обращаться к локальным переменным. Это усложняет работу компилятора. Вот предыдущий пример локального класса:

    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 внутреннего класса. (Поскольку array - final, такие копии никогда не содержат противоречивых значений.)...

Видите ли, разработчики языка хотели, чтобы скопированная локальная переменная была "согласованной" каждый раз при создании такой копии. Их мотивация, скорее всего, заключалась в том, что разработчикам не нужно было беспокоиться о том, чтобы посмотреть вне копии внутреннего класса, чтобы проверить, было ли это изменено:

Enumeration myEnumerate(Object array[], int copy) { // array not final, let 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
    }

Ответ 2

Внутренние классы могут получить доступ к final переменным окружающих классов.


Вот интересная записка:

Собственно, реализация прототипа позволила неконфигурировать переменные, на которые можно ссылаться из внутренних классов. Там было протест от пользователей, жалуясь, что они этого не хотят! Причина была интересной: чтобы поддерживать такие переменные, необходимо было выделить их - и (в то время, по крайней мере) средний программист Java по-прежнему был довольно пугливым в отношении кучи распределение и сбор мусора и все такое. Они отклонили языка, выполняющего распределение кучи "под столом", когда не было появления "нового" ключевого слова в поле зрения.

Ответ 3

Объект анонимного класса может иметь срок службы, который длится дольше, чем метод, который его создал, но он не может длиться дольше, чем время жизни родительского объекта.

Рассмотрим следующее

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

Какую переменную 'a' представляет собой Runnable, которая будет изменяться. Невозможно изменить один локальный метод, потому что этот метод больше не находится в стеке.

Ответ 4

Как сказал в комментариях Рафаэль Осипов, вы определенно можете получить доступ к mSomeVar, переменной экземпляра окружающего класса, из анонимного класса. Поэтому ваш вопрос не имеет смысла.