変数がメソッドdef内にある場合、それはローカル変数です-そして、そのメソッドの実行後に存在し、そのローカル変数の存続期間が終了したオブジェクトをインスタンス化しています(メソッドのスタックフレーム内にあり、メソッドの戻り時に破棄されます)。これがまったく機能するためには、いくつかのコンパイラの魔法が必要であり、これは通常、Javaでの現在の実装が非常に不十分であっても、クロージャと呼ばれます。ActionListener
コンパイラは、ローカル変数の値がコピーされるインスタンス変数を実装し、持つクラスを実際に合成します。
final
スレッドセーフの懸念から、変数を閉じることができるのはJavaに固有の制限です。ここでの話は、ローカル変数は常にスレッドセーフであるということはJava開発者の直感に根付いているということです-メソッドの実行中に変更することはできません(そのメソッドによって明示的に変更が行われることはありません)。非ファイナル変数をクローズすると、現在実行中のメソッドと並行してクローズが実行され、その変数が変更される可能性があるため、これに違反する可能性があります。それはいくつかの非常に直感に反する行動につながるでしょう。
ただし、(やや不十分な)回避策があります。
public void myMethod() {
final JLabel[] e = {new JLabel("")};
JButton b = new JButton("ok");
b.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent arg0) {
e[0].setSize(200,200);
}});
}
e
は1要素の配列になりました。これはヒープ上にあり、メソッドのスタックフレームで破棄されることはありません。また、直感を適用して、スレッドセーフではなくなり、リスナーがその値を完全に異なるものに簡単に変更できることを確認することもできます。e[0]
ここに示すメソッドは、明示的なミューテーションコードがない場合にその変更を監視します。