Я имею, столкнулся со многими ситуация, куда я должен был передать значение другому потоку, и я основал, что я мог сделать это этот путь, но я задавался вопросом, как это работает?
public void method() {
final EventHandler handle = someReference;
Thread thread = new Thread() {
public void run() {
handle.onEvent();
}
};
thread.start();
}
Править: Просто поймите, что мой вопрос точно не указывал на то, что я хотел знать. Это больше, "как" это работает скорее затем "почему".
Вы можете заметить, что происходит внизу, просто декомпилировав внутренний класс. Вот краткий пример:
После компиляции этого фрагмента кода:
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
. Это означает, что дескриптор
копируется как поле val $ handle
во внутренний класс. И это будет работать правильно, только если дескриптор
никогда не изменится - что в Java означает, что он должен быть final
.
Вы также можете заметить, что у внутреннего класса есть поле this $ 0
, которое является ссылкой на внешний класс. Это, в свою очередь, показывает, как нестатические внутренние классы могут взаимодействовать с внешними классами.
Вот более подробное объяснение того, КАК это работает: http://techtracer.com/2008/04/14/mystery-of-accessibility-in-local-inner-classes/
Ни один метод не может получить доступ к локальным переменным других методов. Сюда входят методы анонимных классов, как в вашем примере. То есть метод run
в анонимном классе Thread не может получить доступ к локальной переменной метода method ()
.
Запись final
перед локальной переменной - это способ для вас, как программиста, сообщить компилятору, что переменная на самом деле может рассматриваться как значение. Поскольку эта переменная (считываемое значение!) Не изменится, можно получить к ней доступ другими методами.
Этот внутренний класс транслируется в код, подобный следующему:
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();
Поскольку внутреннему классу фактически передается копия последней переменной, он не может вносить в нее какие-либо изменения, которые были бы видны во внешнем классе. Чтобы запретить даже попытки внести изменения в этот параметр, разрешен доступ только к конечным переменным во внутренних классах.