Окончательное перечисление в методе run () Thread - PullRequest
2 голосов
/ 28 марта 2010

Почему определение Элвиса Элвиса должно быть окончательным для использования внутри метода Thread run ()?

 Elvis elvis = Elvis.INSTANCE; // ----> should be final Elvis elvis = Elvis.INSTANCE
 elvis.sing(4);

 Thread t1 = new Thread(
   new Runnable() {
   @Override
   public void run() {
   elvis.sing(6); // --------> elvis has to be final to compile
  }
}
);


 public enum Elvis {
   INSTANCE(2);

   Elvis() {
     this.x = new AtomicInteger(0);
   }

   Elvis(int x){
     this.x = new AtomicInteger(x);
   }

   private AtomicInteger x = new AtomicInteger(0);

   public int getX() { return x.get(); }

   public void setX(int x) {this.x = new AtomicInteger(x);}

   public void sing(int x) {
      this.x = new AtomicInteger(x);
      System.out.println("Elvis singing.." + x);
   }
 }

Ответы [ 3 ]

3 голосов
/ 28 марта 2010

Значение переменной elvis фиксируется анонимным внутренним классом.

Только Java (в настоящее время) захватывает переменные по значению . Компилятор требует, чтобы переменная была окончательной, чтобы не было путаницы в том, что будет фактически использоваться при вызове метода run в новом потоке: если вы изменили значение elvis после создания нового потока, но до запуская его, что вы ожидаете от него?

Это разница между тем, как замыкания эффективно доступны в C # и Java. См. Мою статью о замыканиях для более подробной информации. Java 7 сделает замыкания более краткими - я не следил за тем, чтобы знать, будет ли какой-либо способ захвата самой переменной, а не определенного значения.

2 голосов
/ 28 марта 2010

Это не имеет никакого отношения к потокам и всему, что связано с созданием анонимных классов. Проблема в том, что вы ссылаетесь на локальную переменную из анонимного класса. Теперь рассмотрим следующее:

int c = 5;
Runnable r = new Runnable(){ public void run(){ System.out.println(c); } };
c = 6;
r.run();

В приведенном выше фрагменте должен ли код печататься 5 или 6? Если r удерживать ссылку на текущий кадр стека для разрешения c, возможно, что он мог бы вывести 6. Также возможно, что он мог связать / захватить значение c ранее и вывести 5. Java-силы Вы должны сделать c final, чтобы прояснить это, а также освободить Java от необходимости зависать на текущем фрейме стека.

1 голос
/ 28 марта 2010

это не часть вашего вопроса, но мне просто любопытно: почему вы переназначаете Elvis.x, если это AtomicInteger? такого рода не хватает для обеспечения безопасности потоков в AtomicInteger. Рассмотрим переписывание:

public Elvis {

   final static private Elvis INSTANCE = new Elvis(2);
   static public Elvis getInstance() { return INSTANCE; }

   final private AtomicInteger x; 


   Elvis() { this(0); }

   Elvis(int x){ 
      this.x = new AtomicInteger(x);
   }

   public int getX() { return this.x.get(); }

   public void setX(int x) {this.x.set(x); }

   public void sing(int x) {
      setX(x);
      System.out.println("Elvis singing.." + x);
   }
 }

также, поскольку это изменяемое содержимое, оно не должно быть перечислением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...