2

Elvis elvis定義をThreadrun()メソッド内で使用するためにfinalにする必要があるのはなぜですか?

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

3 に答える 3

3

変数の値はelvis、匿名の内部クラスによってキャプチャされています。

Javaのみ(現在)は値によって変数をキャプチャします。コンパイラは、新しいスレッドでメソッドが呼び出されたときに実際に何が使用されるかについて混乱しないように、変数がfinalであることを要求しrunます。新しいスレッドを作成した後、開始する前にの値を変更した場合elvis、どのようになりますかそれがすることを期待しますか?

これは、C#とJavaでクロージャを効果的に使用できる方法の違いです。これの詳細については、私のクロージャの記事を参照してください。Java 7はクロージャをより簡潔にします-私は、特定の値ではなく変数自体をキャプチャする方法があるかどうかを知るためにフォローしていません。

于 2010-03-28T12:11:05.620 に答える
2

これはスレッドとは何の関係もなく、匿名クラスの構築と関係があります。問題は、匿名クラス内からローカル変数を参照していることです。ここで、次のことを考慮してください。

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の力これを完全に明確にし、現在のスタックフレームにハングアップする必要性をJavaから解放するために、cをfinalにします。

于 2010-03-28T12:17:36.203 に答える
1

これはあなたの質問の一部ではありませんが、ちょっと興味があります: 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);
   }
 }

また、これには変更可能なコンテンツがあるため、列挙型であってはなりません。

于 2010-03-28T12:29:22.723 に答える