3

メソッド内に次の内部クラス (IsSomething) があるとします。

public class InnerMethod {

private int x;


public class Something {
    private int y;

    public void printMyNumber(double x)
    {
        class IsSomething extends Something {

            public void print() {
                System.out.println(x);
            }

        }
    }
}

}

X変数を機能させるには、なぜX変数をFINALにする必要があるのですか..? (「printMyNumber」関数の X パラメータについて話しています。)

4

3 に答える 3

1

匿名クラスのメソッドは、実際にはローカル変数とメソッド パラメーターにアクセスできません。代わりに、匿名クラスのオブジェクトがインスタンス化されると、オブジェクトのメソッドによって参照される最終的なローカル変数とメソッド パラメーターのコピーがインスタンス変数としてオブジェクトに格納されます。匿名クラスのオブジェクトのメソッドは、これらの非表示のインスタンス変数に実際にアクセスします。

JLS から:

Any local variable, formal method parameter or exception handler parameter used but not declared in an inner class must be declared final. Any local variable, used but not declared in an inner class must be definitely assigned (§16) before the body of the inner class.

于 2013-04-15T05:38:34.043 に答える
1

違いは、ローカル変数とクラス メンバー変数の違いです。メンバー変数は、囲んでいるオブジェクトの存続期間中に存在するため、内部クラスのインスタンスから参照できます。ただし、ローカル変数はメソッドの呼び出し中にのみ存在し、その暗黙的なコピーが内部クラスのメンバーとして生成されるという点で、コンパイラによって異なる方法で処理されます。ローカル変数 final を宣言しないと、それを変更でき、内部クラスが依然としてその変数の元の値を参照しているため、微妙なエラーが発生する可能性があります。

最終ローカル変数

ローカル変数またはパラメーターを final にする理由は 2 つあります。最初の理由は、コードでローカル変数またはパラメーターを変更したくないからです。コードが不明確になるため、メソッド内のパラメーターを変更するのは悪いスタイルであると多くの人が考えています。一部のプログラマーは、習慣として、すべてのパラメーターを「最終」にして、自分でパラメーターを変更できないようにしています。メソッドの署名が少し見苦しくなるので、私はそうしません。

2 つ目の理由は、内部クラス内からローカル変数またはパラメーターにアクセスする場合です。これが、私の知る限り、JDK 1.1 で最終的なローカル変数とパラメーターが Java 言語に導入された実際の理由です。

public class Access1 {
  public void f() {
    final int i = 3;
    Runnable runnable = new Runnable() {
    public void run() {
      System.out.println(i);
    }
    };
  }
}

run() メソッド内では、外部クラスで final にした場合にのみ i にアクセスできます。その理由を理解するには、

コンパイラが何をするか見てください。Access1.class と Access1$1.class の 2 つのファイルが生成されます。それらを JAD で逆コンパイルすると、次のようになります。

public class Access1 {
  public Access1() {}
  public void f() {
    Access1$1 access1$1 = new Access1$1(this);
  }
}

class Access1$1 implements Runnable {
  Access1$1(Access1 access1) {
    this$0 = access1;
  }
  public void run() {
    System.out.println(3);
  }
  private final Access1 this$0;
}

i の値は final であるため、コンパイラはそれを内部に「インライン化」できます。

クラス。上記を見るまで、ローカル変数が内部クラスによってアクセスされるために最終的でなければならないことに私は動揺しました。

ローカル変数の値が内部クラスのさまざまなインスタンスで変化する可能性がある場合、コンパイラはそれを内部クラスのデータ メンバーとして追加し、コンストラクターで初期化できるようにします。この背後にある根本的な理由は、Java には C のようなポインターがないことです。

次のクラスを検討してください。

public class Access2 {
  public void f() {
    for (int i=0; i<10; i++) {
    final int value = i;
    Runnable runnable = new Runnable() {
      public void run() {
        System.out.println(value);
      }
    };
    }
  }
} 

ここでの問題は、for ループを通過するたびに新しいローカル データ メンバーを作成する必要があることです。

コーディング中に、上記のコードを次のように変更する必要がありました。

public class Access3 {
  public void f() {
    Runnable[] runners = new Runnable[10];
    for (final int[] i={0}; i[0]<runners.length; i[0]++) {
    runners[i[0]] = new Runnable() {
      private int counter = i[0];
      public void run() {
        System.out.println(counter);
      }
    };
    }
    for (int i=0; i<runners.length; i++)
    runners[i].run();
  }
  public static void main(String[] args) {
    new Access3().f();
  }
}

追加の final ローカル変数を宣言する必要がなくなりました。実際、それはおそらく真実ではないでしょうか?

int[] i は int への一般的な C ポインターのようなものですか? これを見るのに4年かかりましたが、このアイデアをどこかで聞いたことがあるなら、あなたから聞いてみたい.

于 2013-04-15T05:39:06.177 に答える
0

これは、ローカル クラスのインスタンスの有効期間が、そのクラスが定義されているメソッドの実行よりもはるかに長くなる可能性があるためです。このため、ローカル クラスには、使用するすべてのローカル変数のプライベート内部コピーが必要です (これらのコピーはコンパイラによって自動的に生成されます)。ローカル変数とプライベート コピーが常に同じであることを保証する唯一の方法は、ローカル変数が final であることを主張することです。

于 2013-04-15T05:44:34.063 に答える