web-dev-qa-db-ja.com

「別のメソッドで定義された内部クラス内の非最終変数iを参照できない」と表示されるのはなぜですか?

ボタンクリックリスナーがあり、onCreate()メソッドに次のようなローカル変数があります

 onCreate() {

 super.onCreate();

 int i = 10;

 Button button = (Button)findViewById(R.id.button);

 button.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            i++;
        }   
    });

なぜJavaは私を最終にするように要求するのですか?

14
Khawar Raza

OnCreate()メソッドが戻ると、ローカル変数がスタックからクリーンアップされるため、ローカル変数は存在しなくなります。ただし、匿名クラスオブジェクトのnew View.OnClickListener()は、これらの変数を参照します。原因は間違った動作なので、Javaこれを許可しないでください。

最終的な後は定数になります。したがって、ヒープに格納され、匿名クラスで安全に使用できます。

26
birdy

匿名内部クラスは、ローカル変数のコピーを取得することにより、その囲んでいるスコープを参照します。匿名内部クラスのintの値を変更する場合は、ハックを行う必要があります。

final int[] arr = new int[1];
arr[0] = 10;
Button button = (Button)findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener() {
  arr[0]++;
}
13
hrnt

匿名の内部クラス内でアクセスしているためです。あなたはそれをすることはできません。最終的にしてからread匿名の内部クラスから取得できますが、インクリメントすることはできません。

オプション:

  • 代わりに、外部クラスのインスタンス変数にします
  • 代わりに、匿名内部クラスのインスタンス変数にします
  • ラッパーを使用します-例:単一要素の配列、AtomicIntegerなど

他の場所からiにアクセスする必要がない限り、おそらく2番目のオプションを優先します。正直に言うと、3番目のオプションは少し厄介なハックだと思います。

12
Jon Skeet

変数finalを作成する必要があります。これは、内部では、そのような匿名の内部クラスが単純に 構文糖衣構文 であり、ネストされたクラスoutsideにコンパイルされるためです。 )包含メソッドのスコープ。

これは、メソッド内で宣言された変数のいずれも内部クラスにアクセスできないことを意味します。そのため、コンパイラーは別のトリックをプルします-そしてコピーの値クラスの非表示コンストラクター。メソッド内の変数への変更に一致するようにこのコピーが更新されないプログラマーの混乱を避けるために、そのような変更がないことを確認するのは最終的なものでなければなりません。

ここでの目標はインクリメントする整数を使用することであり、参照のみが最終である必要があるため(オブジェクト自体は不変である必要はありません)、final AtomicInteger iを宣言し、コールバックから必要に応じてインクリメントできます。

5

i変数をインクリメントする必要があるため、最終的にすることはできません。代わりに、クラスメンバーにすることができます。

4
dbm