Ich war überrascht zu sehen, dass das folgende Java-Code-Snippet kompiliert und ausgeführt wurde:
for(final int i : listOfNumbers) {
System.out.println(i);
}
wobei listOfNumbers ein Array von ganzen Zahlen ist.
Ich dachte, Schlusserklärungen würden nur einmal vergeben. Erstellt der Compiler ein Integer-Objekt und ändert es, worauf es verweist?
Stellen Sie sich vor, dass die Kurzschrift ungefähr so aussieht:
for (Iterator<Integer> iter = listOfNumbers.iterator(); iter.hasNext(); )
{
final int i = iter.next();
{
System.out.println(i);
}
}
Siehe @TravisG für eine Erläuterung der betroffenen Scoping-Regeln. Der einzige Grund, warum Sie eine Endschleifenvariable wie diese verwenden würden, ist, wenn Sie sie in einer anonymen inneren Klasse schließen müssten.
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();
}
}
}
Die Schleifenvariable ist nicht im Gültigkeitsbereich des Runnable, wenn sie ausgeführt wird, sondern nur, wenn sie instanziiert wird. Ohne das final
Schlüsselwort wäre die Schleifenvariable für das Runnable nicht sichtbar, wenn es schließlich ausgeführt wird. Selbst wenn es so wäre, wäre es derselbe Wert für alle Runnables.
Übrigens, vor ungefähr 10 Jahren haben Sie vielleicht eine sehr kleine Geschwindigkeitsverbesserung bei der Verwendung von final für eine lokale Variable gesehen (in einigen seltenen Fällen); das ist schon lange nicht mehr so. Nun ist der einzige Grund, final zu verwenden, Ihnen zu erlauben, einen lexikalischen Abschluss wie diesen zu verwenden.
Als Antwort auf @mafutrct:
Wenn Sie Code schreiben, tun Sie dies für zwei Zielgruppen. Der erste ist der Computer, der, solange er syntaktisch korrekt ist, mit allen Entscheidungen zufrieden sein wird, die Sie treffen. Die zweite ist für einen zukünftigen Leser (häufig Sie selbst), hier ist das Ziel, die „Absicht“ des Codes zu kommunizieren, ohne die „Funktion“ des Codes zu verschleiern. Sprachmerkmale sollten idiomatisch mit so wenigen Interpretationen wie möglich verwendet werden, um Mehrdeutigkeiten zu reduzieren.
Im Fall der Schleifenvariablen könnte die Verwendung von final verwendet werden, um eines von zwei Dingen zu kommunizieren: Einzelzuweisung; oder Schließung. Ein einfacher Scan der Schleife wird Ihnen sagen, ob die Schleifenvariable neu zugewiesen wurde; Aufgrund der Verschachtelung zweier Ausführungspfade beim Erstellen einer Closure kann jedoch leicht übersehen werden, dass die Closure die Variable erfassen soll. Es sei denn natürlich, Sie verwenden final immer nur, um eine Erfassungsabsicht anzugeben, an welcher Stelle dem Leser klar wird, was passiert.