19

Java の匿名クラスについて読んでいますが、囲んでいるクラスのメソッドにはアクセスできますが、ローカル変数にはアクセスできないと書かれています。なぜこのようになっているのですか?私はこれについて話している:

編集: 古い例は間違っていて、私の意図を反映していませんでした。これは、「囲んでいるクラスのメンバーへのアクセス」セクションhttp://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.htmlに書かれている内容によると、より良い例になるはずです。

public class MyClass {
    public interface SomeInterface{
        public void someOtherMethod();
    }

    public void someMethod(int someLocalVar) {
        SomeInterface myClass = new SomeInterface(){
            public void someOtherMethod(){
                someLocalVar = 0; // This must be final to work
            }
        }
    }
}

では、この制限はどのような問題を解決するのでしょうか?

4

3 に答える 3

17

これは、初期バージョンの Java 内部クラス仕様に由来します。

VM 仕様 2.14などから参照される公式仕様 URL は、リンクの腐敗のためになくなりました: http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html

1999 年 1 月 17 日のスナップショットは wayback マシンで取得できますが、それぞれの仕様セクションはローカル変数への参照 です

物事がどのように機能するかは、次のように説明されています (最も関連性の高いステートメントを太字で示しています)。

ブロックに対してローカルなクラス定義は、ローカル変数にアクセスできます。これは、コンパイラの仕事を複雑にします。ローカル クラスの前の例を次に示します。

    Enumeration myEnumerate(final Object array[]) {
        class E implements Enumeration {
            int count = 0;
            public boolean hasMoreElements()
                { return count < array.length; }
            public Object nextElement() {
                { return array[count++]; }
        }
        return new E();
    }

ローカル変数を内部クラスのメソッドから見えるようにするために、コンパイラは内部クラスがアクセスできる場所に変数の値をコピーする必要があります。同じ変数への参照は、どこでも同じ値が生成される限り、さまざまな場所でさまざまなコード シーケンスを使用できます。これにより、その名前は、そのスコープのすべての部分で一貫して同じ変数を参照しているように見えます。

慣例により、ローカル変数 likeは内部クラスのarrayプライベート フィールドにコピーされます。val$array( arrayisfinalであるため、そのようなコピーには一貫性のない値が含まれることはありません。) ...

おわかりのように、言語設計者は、そのようなコピーが作成されるたびに、コピーされたローカル変数の値が「一貫している」ことを望んでいました。彼らの動機は、おそらく、開発者が内部クラスのコピーの外側を見て、変更されているかどうかを確認する必要がなくなることです。

Enumeration myEnumerate(Object array[], int copy) { // array not final, let's see...
    for (int i = 0, i < 2; i++ ) { // loop to have several copies
        class E implements Enumeration {
            int count = 0;
            public boolean hasMoreElements()
                { return count < array.length; }
            public Object nextElement() {
                { return array[count++]; }
        } // we hope to be done with E... oh no 

        array = null; // not final => can change

        if (i == copy) {
            return new E(); // we need to look outside of E
            // to figure value of array it uses
        }
    }
    return null;
}

仕様の例では名前付きクラスを使用していますが、匿名クラスにも同じ理由が適用されることに注意してください。

// ...
    for (int i = 0, i < 2; i++ ) { // loop to have several copies
        if (i == copy) {
            return new Enumeration() {
                int count = 0;
                public boolean hasMoreElements()
                    { return count < array.length; }
                public Object nextElement() {
                    { return array[count++]; }
            } // we hope to be done... oh no
        }

        array = null; // not final => can change
    }
于 2013-07-16T20:11:55.760 に答える
11

内部クラスはfinal、囲んでいるクラスの変数にアクセスできます。


ここに興味深いメモがあります:

実際、プロトタイプの実装で、非 final 変数を内部クラス内から参照できました。ユーザーからは、これが欲しくないとの抗議がありました。その理由は興味深いものでした。そのような変数をサポートするには、それらをヒープに割り当てる必要があり、(少なくとも当時は) 平均的な Java プログラマーは、ヒープの割り当てやガベージ コレクションなどについてまだかなり慎重でした。彼らは、「新しい」キーワードの出現が見えないときに「テーブルの下」でヒープ割り当てを実行する言語を承認しませんでした。

于 2013-07-16T20:29:08.957 に答える
0

匿名クラス オブジェクトの有効期間は、それを作成したメソッドより長くなる場合がありますが、親オブジェクトの有効期間より長くなることはありません。

次のことを考慮してください

public void runSomething() {
   int a = 5;
   new Thread(new Runnable() {
      public void run() {
         a = 10;
      }
   }
}

Runnable が変更しようとしている「a」変数はどれか。メソッドがスタック上にないため、メソッドに対してローカルなものを変更できません。

于 2013-07-16T17:43:37.180 に答える