web-dev-qa-db-ja.com

「final int i」は、Java forループ内でどのように機能しますか?

次のJavaコードスニペットがコンパイルされて実行されたことを確認して驚いた。

for(final int i : listOfNumbers) {
     System.out.println(i);
}

ここで、listOfNumbersは整数の配列です。

私は最終的な宣言が一度だけ割り当てられると思いました。コンパイラーはIntegerオブジェクトを作成し、それが参照するものを変更していますか?

60
Abe

略記が次のようになっていると想像してください。

for (Iterator<Integer> iter = listOfNumbers.iterator(); iter.hasNext(); )
{
    final int i = iter.next();
    {
        System.out.println(i);
    }
}
66
Travis Gockel

関連するスコープルールの説明については、@ TravisGを参照してください。このような最終ループ変数を使用する唯一の理由は、匿名の内部クラスでそれを閉じる必要がある場合です。

import Java.util.Arrays;
import Java.util.List;

public class FinalLoop {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(new Integer[] { 0,1,2,3,4 });
        Runnable[] runs = new Runnable[list.size()];

        for (final int i : list) {
            runs[i] = new Runnable() {
                    public void run() {
                        System.out.printf("Printing number %d\n", i);
                    }
                };
        }

        for (Runnable run : runs) {
            run.run();
        }
    }
}

ループ変数は、インスタンス化されたときのみ、実行時にRunnableのスコープ内にありません。 finalキーワードがないと、ループ変数は最終的に実行されるときにRunnableからは見えません。たとえそうであっても、すべてのRunnableで同じ値になります。

ところで、約10年前に、ローカル変数でfinalを使用すると、速度がわずかに向上する場合があります(まれな場合があります)。それは長い間そうではありませんでした。今度はfinalを使用する唯一の理由は、このような字句クロージャを使用できるようにすることです。

@mafutrctへの回答:

コードを書くときは、2人の対象者のために行います。 1つ目は、構文的に正しい限り、どのような選択でも満足できるコンピューターです。 2つ目は、将来の読者(多くの場合は自分自身)向けであり、ここでの目的は、コードの「機能」を不明瞭にせずに、コードの「意図」を伝えることです。言語機能は、あいまいさを減らすために、できるだけ少ない解釈で慣用的に使用する必要があります。

ループ変数の場合、finalを使用すると、次の2つのいずれかを伝達できます。または、閉鎖。ループの簡単なスキャンは、ループ変数が再割り当てされているかどうかを教えてくれます。ただし、クロージャを作成するときに2つの実行パスをインターリーブするため、クロージャが変数をキャプチャすることを意図していたことを見逃しがちです。もちろん、キャプチャする意図を示すためにfinalを使用する場合を除いて、その時点で読者に何が起こっているかが明らかになります。

14
Recurse