2012-07-11 17 views
9

Si tengo una variable int x = 1, por ejemplo, y declaro un ejecutable en el hilo principal, y quiero pasar x al método run() del ejecutable, debe declararse final. ¿Por qué?¿Por qué las variables pasadas a ejecutable deben ser definitivas?

final int x = 0;//<----must be final... 
private class myRun implements Runnable { 

    @Override 
    public void run() { 
     x++;// 
    } 

} 
+1

Porque así es como se define el idioma. Presumiblemente para mantener las * variables * modificadas dentro de dicho método en la * clase interna anónima *. (Creo que también simplifica la implementación: solo los * valores * necesitan ser copiados de manera proxy en el tipo anónimo y las variables originales ya no deben mantenerse, como sería necesario con la semántica de cierre completo). –

+0

Si esto no era el caso, sus variables podrían modificarse en cualquier momento sin previo aviso. – jahroy

Respuesta

8

Porque si ellos son capaces de ser cambiado, podría causar muchos problemas, considere esto:

public void count() 
{ 
    int x; 

    new Thread(new Runnable() 
    { 
     public void run() 
     { 
      while(x < 100) 
      { 
       x++; 
       try 
       { 
        Thread.sleep(1000); 
       }catch(Exception e){} 
      } 
     } 
    }).start(); 

    // do some more code... 

    for(x = 0;x < 5;x++) 
     for(int y = 0;y < 10;y++) 
      System.out.println(myArrayElement[x][y]); 
} 

Este es un ejemplo aproximado, pero se puede ver un montón de errores inexplicables. Esta es la razón por la cual las variables deben ser finales. Aquí es una solución simple para el problema anterior:

public void count() 
{ 
    int x; 

    final int w = x; 

    new Thread(new Runnable() 
    { 
     public void run() 
     { 
      int z = w; 

      while(z < 100) 
      { 
       z++; 
       try 
       { 
        Thread.sleep(1000); 
       }catch(Exception e){} 
      } 
     } 
    }).start(); 

    // do some more code... 

    for(x = 0;x < 5;x++) 
     for(int y = 0;y < 10;y++) 
      System.out.println(myArrayElement[x][y]); 
} 

Si desea una explicación más completa, es algo así como sincronizado. Java quiere evitar que hagas referencia a un Objeto de múltiples Hilos. Aquí está un poco acerca de la sincronización:

Espero que esto ayudó!

+1

Esto no causaría muchos problemas - muchos idiomas tienen cierres completos :-) El hecho de que Runnable (y probablemente hilos) se encuentre en el tema aquí es ortogonal para permitir la modificación de variables locales a partir de tipos internos anónimos. Incluso si se habla de algo que no se trata de enhebrar, serían las mismas reglas del lenguaje. Además, el mismo problema de subprocesamiento sería el resultado de las variables miembro. –

+1

En su "Aquí hay una solución simple para el problema anterior:", ¿w ++ no le dará un error de compilación? No puede reasignar w después de inicializarlo. – mskw

+0

@mskw No puedo creer que nadie haya dicho nada hasta ahora, gracias por encontrarlo. – John

6

Porque eso es lo que dice la especificación del lenguaje. According to Guy Steele, la razón detrás de esta elección es que los programadores esperarían la declaración int x = 0 en un método para generar almacenamiento asignado a la pila, pero si puede devolver new myRun() del método (o deje que myRun persista después del retorno de la función) y usted puede modificarlo luego, entonces x tiene que ser asignado en el montón para tener la semántica que esperas.

Podrían haber hecho eso, y de hecho otros idiomas lo han hecho de esa manera. Pero los diseñadores de Java decidieron requerir que marque x como final para evitar requerir implementaciones para distribuir de forma apilable lo que parece almacenamiento almacenado en la pila.

(deben tener en cuenta: esto no es específico de Runnable Se aplica a cualquier clase interna anónima..)

+0

Ah, sí, tienes razón, he leído mal la pregunta. – jacobm

+0

+1 para "it [* is not *] specific to Runnable" –

1

El gran 'problema' con el multihilo, y también la razón por la que se usa, es que varias cosas están sucediendo al mismo tiempo. De repente, el valor de cualquier variable a la que acceda el hilo que no sea local al hilo puede cambiar en cualquier momento. Por lo tanto, es posible que sólo estás impresión de los números 1-10 con este código:

int x = 0; //supposing that this was allowed to be non-final... 
    private class myRun implements Runnable{ 

    @Override 
    public void run() { 
     for (int i=0; i<10; i++) { 
      System.Out.Println(x++); 
     } 
    } 
} 

Pero en realidad, si otro código en esa clase cambia el valor de x, que podría terminar la impresión 230498-230508 .El valor de x podría cambio de evento en el medio de su ciclo. Si no puede confiar en x que tiene un cierto valor o mantener el valor que le asignó anteriormente, se vuelve inútil usarlo en su código. ¿Por qué usarías una variable si su contenido pudiera cambiar con un simple golpe?

En lugar de solo prohibirte usarlo, Java requiere que lo hagas final. Podrías simplemente 'prometer' nunca cambiar el valor de x de otro hilo, pero ¿por qué no hacerlo final en primer lugar y dejar que el compilador te ayude?De acuerdo, solo puede acceder al valor inicial asignado al x, pero el solo hecho de poder acceder al valor inicial de la variable es mejor que no poder usarlo en absoluto, lo que efectivamente cortaría la capacidad del subproceso para utilizar los datos del resto de tu clase

Cuestiones relacionadas