Что именно происходит, когда у вас есть конечные значения и внутренние классы в методе? - PullRequest
11 голосов
/ 27 июня 2010

Я сталкивался со многими ситуациями, когда мне нужно было передать значение другому потоку, и я понял, что могу сделать это таким образом, но мне было интересно, как это работает?

public void method() {
    final EventHandler handle = someReference;

    Thread thread = new Thread() {
        public void run() {
            handle.onEvent();
        }
    };

    thread.start();
}

Редактировать: Просто поймите, что мой вопрос не совсем указывает на то, что я хотел знать.Это скорее «как», а не «почему».

Ответы [ 4 ]

9 голосов
/ 27 июня 2010

Вот более подробное объяснение того, КАК это работает: http://techtracer.com/2008/04/14/mystery-of-accessibility-in-local-inner-classes/

8 голосов
/ 27 июня 2010

Ни один метод не может получить доступ к локальным переменным других методов. Это включает в себя методы анонимных классов, как в вашем примере. То есть метод run в анонимном классе Thread не может получить доступ к локальной переменной method().

Запись final перед локальной переменной - это способ для вас, как программиста, сообщить компилятору, что переменная действительно может рассматриваться как значение. Поскольку эта переменная (читай значение!) Не изменится, доступ к ней в других методах возможен.

7 голосов
/ 27 июня 2010

Вы можете заметить, что происходит внизу, просто декомпилируя внутренний класс. Вот краткий пример:

После компиляции этого фрагмента кода:

public class Test {

 public void method() {
     final Object handle = new Object();

     Thread thread = new Thread() {
         public void run() {
             handle.toString();
         }
     };

     thread.start();
 }
}

вы получите Test.class для Test.java и Test$1.class для внутреннего класса в Test.java. После декомпиляции Test$1.class вы увидите это:

class Test$1 extends Thread
{
  final Test this$0;
  private final Object val$handle;

  public void run()
  {
    this.val$handle.toString();
  }
}

Как видите, вместо handle переменная this.val$handle. Это означает, что handle копируется как поле val$handle во внутренний класс. И это будет работать правильно, только если handle никогда не изменится - что в Java означает, что это должно быть final.

Вы также можете заметить, что у внутреннего класса есть поле this$0, которое является ссылкой на внешний класс. Это, в свою очередь, показывает, как нестатические внутренние классы могут взаимодействовать с внешними классами.

2 голосов
/ 27 июня 2010

Этот внутренний класс переводится в код, подобный следующему:

class InnerClass extends Thread {
    private EventHandler handle;

    public InnerClass(EventHandler handle) {
        this.handle = handle;
    }

    public void run() {
        handle.onEvent();
    }
}

...

EventHandler handle = someReference;
Thread thread = new InnerClass(handle);
thread.start();

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

...