2010-10-12 12 views
59

Me sorprendió ver que el siguiente fragmento de código Java compilado y corrieron:¿Cómo funciona "int final" dentro de un bucle for Java?

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

donde ListOfNumbers es una matriz de enteros.

Pensé que las declaraciones finales se asignaban solo una vez. ¿El compilador crea un objeto Integer y cambia lo que hace referencia?

Respuesta

66

Imagínese que la taquigrafía se parece mucho a esto:

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

Ver @TravisG para una explicación de las reglas de alcance involucrados. La única razón por la que usaría una variable de bucle final como esta es si necesita cerrarla en una clase interna anónima.

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(); 
     } 
    } 
} 

variables El bucle no está en el alcance de la Ejecutable cuando se ejecuta, sólo cuando se crea una instancia. Sin la palabra clave final, la variable de bucle no sería visible para Runnable cuando finalmente se ejecute. Incluso si lo fuera, sería el mismo valor para todos los Runnables.

Por cierto, hace aproximadamente 10 años es posible que haya visto una mejora de velocidad muy pequeña en el uso final en una variable local (en algunas ocasiones excepcionales); ese no ha sido el caso durante mucho tiempo. Ahora, la única razón para utilizar el final es permitirte usar un cierre léxico como este.

En respuesta a @mafutrct:

Al escribir código, lo hace a dos audiencias. La primera es la computadora que, siempre que sea sintácticamente correcta, estará contenta con las elecciones que haga. El segundo es para un lector futuro (a menudo usted mismo), aquí el objetivo es comunicar la 'intención' del código, sin oscurecer la 'función' del código. Las características del lenguaje se deben usar idiomáticamente con la menor cantidad posible de interpretaciones para reducir la ambigüedad.

En el caso de la variable de bucle, el uso de final se puede usar para comunicar cualquiera de dos cosas: asignación única; o, cierre. Un escaneo trivial del ciclo le dirá si la variable de ciclo se reasigna; sin embargo, debido a la intercalación de dos rutas de ejecución al crear un cierre, puede ser fácil pasar por alto que el cierre pretendía capturar la variable. A menos que, por supuesto, solo utilice el final para indicar la intención de capturar, momento en el que se vuelve obvio para el lector que es lo que está sucediendo.

+7

O para documentar la intención de no hacer reasignable esta variable. –

+2

¡No! Si haces esto, ahora tienes que tener significados semánticos para la misma sintaxis; uno tiene usos reales; el otro es redundante a menos que su método sea demasiado, muy largo. Solo use "final" para indicar que tiene la intención de cerrar el parámetro, _never_ para indicar una sola asignación. – Recurse

+0

@Recurse Eso suena razonable. ¿Podrías explicar el razonamiento detrás de esto? No estoy seguro de entender los motivos – mafu

Cuestiones relacionadas