web-dev-qa-db-ja.com

ループ内に最終的な変数を作成する

これはJavaで許可されていますか:

for(int i=0;i<5;i++){
  final int myFinalVariable = i;
}

私の質問のキーワードはfinalです。ループの実行ごとに変化する最終的な変数を実行することはできますか? finalは変数の値を変更できない(myFinalVariable = iのみを呼び出す)と言っていますが、変数全体をfinal intで再定義しているので、これは不思議でした。

それらは、同じ名前の2つの完全に異なる変数ですか?ループの前の実行からの変数は、すでにガベージコレクターに向かっていますか?

47
Jens Jansson

はい、許可されています。 finalキーワードは、変数の値スコープ内を変更できないことを意味します。ループの例では、変数がループの下部でスコープ外に出て、ループの上部で新しい値でスコープに戻ると考えることができます。ループ内の変数への割り当ては機能しません。

82
Greg Hewgill

そうです、ループの各反復で、新しい変数を作成しています。変数は同じ名前を共有しますが、それらは同じスコープ内にないので問題ありません。次の例はnotです:

final int myFinalVariable = 0;
for(int i=0;i<5;i++){
  myFinalVariable = i;
}
12
tehvan

変数はスタック上の単なる場所です。可能な限り小さなスコープで変数を保持し、最終的にするようにしてください。ただし、スコープとファイナルはソースコードのものにすぎません...コード生成/ VMの観点からは、それらはまったく重要ではありません。

特定の例では、 "int"を使用するとガベージは作成されません。ただし、作成中のオブジェクトの場合は、どちらの場合もごみの量と、ごみがクリーンアップに適格になる時期は同じになります。

次のコードを見てください。

public class X
{
    public static void main(final String[] argv)
    {
        foo();
        bar();
    }

    private static void foo()
    {
        for(int i=0;i<5;i++)
        {
            final int myFinalVariable = i;
        }
    }

    private static void bar()
    {
        for(int i=0;i<5;i++)
        {
            int myFinalVariable = i;
        }
    }
}

コンパイラーは、各メソッドに対して同一のバイトコードを生成します。

public class X extends Java.lang.Object{
public X();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method Java/lang/Object."<init>":()V
   4:   return

public static void main(Java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method foo:()V
   3:   invokestatic    #3; //Method bar:()V
   6:   return

private static void foo();
  Code:
   0:   iconst_0
   1:   istore_0
   2:   iload_0
   3:   iconst_5
   4:   if_icmpge       15
   7:   iload_0
   8:   istore_1
   9:   iinc    0, 1
   12:  goto    2
   15:  return

private static void bar();
  Code:
   0:   iconst_0
   1:   istore_0
   2:   iload_0
   3:   iconst_5
   4:   if_icmpge       15
   7:   iload_0
   8:   istore_1
   9:   iinc    0, 1
   12:  goto    2
   15:  return

}

ループの外側で変数を宣言する別のメソッドを追加すると、変数が宣言される順序のために、バイトコードがわずかに異なります)。このバージョンでは、変数をfinalにすることはできません。この最後のバージョンは最善の方法ではありません(ループ内の最後の変数は、可能であれば最高です)。

private static void car()
{
    int myFinalVariable;

    for(int i=0;i<5;i++)
    {
        myFinalVariable = i;
    }
}

private static void car();
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   iconst_5
   4:   if_icmpge       15
   7:   iload_1
   8:   istore_0
   9:   iinc    1, 1
   12:  goto    2
   15:  return

}
9
TofuBeer

答えは、はい、確かにループ内の変数を「最終」としてマークできます。これがその効果です(Java 7、Eclipse Indigo、Mac OS X Lion)。

for ( int i = 0; i < 5; i++ ) {

  // With 'final' you cannot assign a new value.
  final int myFinalVariable = i;  // Gets 0, 1, 2, 3, or 4 on each iteration.
  myFinalVariable = 7; // Compiler error: The final local variable myFinalVariable cannot be assigned.

  // Without 'final' you can assign a new value.
  int myNotFinalVariable = i;  // Gets 0, 1, 2, 3, or 4 on each iteration.
  myNotFinalVariable = 7; // Compiler is OK with re-assignment of variable's value.

}
2
Basil Bourque

ループ内で宣言された変数のスコープは、ループが1回実行されるまでのみです。

変数をループ内でfinalとして宣言しても、ループ内の変数に違いはありませんが、final修飾子を使用してループの外側で変数を宣言すると、プリミティブ型に割り当てられた値または参照変数に割り当てられたObjectを変更できません。

以下の例では、最初の2つのループに問題はなく、両方のループで同じ出力が得られますが、3番目のループではコンパイル時にエラーが発生します。

公開クラステスト{

public static void main(String[] args) {
    for (int i = 0; i < 5; i++) {
        final int j= i;
        System.out.println(j);
    }
    for (int i = 0; i < 5; i++) {
        int j= i;
        System.out.println(j);
    }

    final int j;
    for (int i = 0; i < 5; i++) {
        j= i;
        System.out.println(j);
    }
}

}

私が間違っているなら、私を訂正してください。

2
Setu Poddar