2009-08-08 11 views
6

Estaba tratando de encontrar casos de prueba oscuros para una JVM de código abierto alternativa con la que estoy ayudando (Avian) cuando encontré un código de código interesante, y me sorprendió que no funcionara 't compilación:variable no inicializada de Java con curiosidad por fin

public class Test { 
    public static int test1() { 
     int a; 
     try { 
      a = 1; 
      return a; // this is fine 
     } finally { 
      return a; // uninitialized value error here 
     } 
    } 
    public static void main(String[] args) { 
     int a = test1(); 
    } 
} 

la ruta de código más obvia (el único que veo) es ejecutar a = 1, 'intento' para devolver una (la primera vez), luego ejecutar el último, el que en realidad devuelve a. Sin embargo, javac se queja de que "a" puede que no se han inicializado:

 
    Test.java:8: variable a might not have been initialized 
     return a; 
      ^ 

La única cosa que puedo pensar en que podría causar/liberar una ruta de código diferente es si una excepción de tiempo de ejecución oscura se produjera después del inicio de el intento, pero antes de que el valor 1 se asigne a un - algo parecido a un OutOfMemoryError o StackOverflowException, pero no puedo pensar en ningún caso donde esto podría ocurrir en este lugar en el código.

¿Alguien más familiarizado con los detalles del estándar Java arroja algo de luz sobre esto? ¿Es solo un caso en el que el compilador es conservador y, por lo tanto, se niega a compilar lo que de otro modo sería un código válido, o algo extraño sucede aquí?

Respuesta

10

Puede parecer contrario a la intuición de que una excepción podría ocurrir en la línea de a = 1, pero podría producirse un error de JVM. Por lo tanto, dejando la variable sin inicializar. Entonces, el error del compilador tiene mucho sentido. Esto es oscuro error de tiempo de ejecución que usted mencionó. Sin embargo, yo diría que un OutOfMemoryError está lejos de ser oscuro y los desarrolladores deberían pensarlo al menos.Además, recuerde que el estado que configura el OutOfMemoryError podría ocurrir en otro hilo y la única acción que empuja la cantidad de memoria del montón usada más allá del límite es la asignación de la variable a.

De todos modos, dado que está buscando el diseño del compilador, también estoy asumiendo que ya sabe lo tonto que es devolver los valores en un bloque finally.

+0

+1 Este es un buen punto. –

+0

Estaba haciendo mi mejor esfuerzo para ejercitar el código de jSR (Jump SubRoutine) en Avian (para esto, tuve que usar ECJ, porque sun javac raramente genera JSRs más), y el caso de prueba del que condensé este ejemplo fue significativamente más Complicado.Estoy muy consciente de la inutilidad de dicho código, pero si se puede compilar, Avian debe ejecutarlo correctamente. –

0

El compilador está siendo conservador aquí.

+0

En realidad, el compilador ES OBLIGATORIO ser conservador aquí; ver la respuesta de @Msaeed. –

3

Creo que es solo por la semántica de una relación try-catch-finally. Desde el Java Language Specification:

Si la ejecución del bloque try se completa con normalidad, a continuación, se ejecuta el bloque finalmente ...

Si la ejecución del bloque try finaliza bruscamente debido a un tiro de un valor V ...

Si la ejecución del bloque try finaliza abruptamente por cualquier otra razón R, entonces el bloque finally se ejecuta ...

El último caso parece ser el más relevante aquí. Parece que el bloque finally debería poder ejecutarse correctamente si el bloque try finaliza abruptamente por CUALQUIER motivo. Obviamente, si el bloque try finaliza antes de la asignación, el bloque finally no sería válido. Sin embargo, como dijiste, esto no es particularmente probable.

2

Es muy probable que javac sea necesario para suponer que puede producirse una excepción en cualquier punto del bloque try, incluso durante la asignación, y que, por lo tanto, finalmente puede devolver una variable no inicializada. En teoría, podría hacer un análisis detallado y descubrir que en todas las rutas a través del bloque try 'a' siempre se inicializaría con éxito, pero eso es mucho trabajo casi sin ganancia.

Ahora bien, si alguien sólo puede señalar el apartado correspondiente en la especificación del lenguaje Java ...

+1

Este comportamiento está especificado por la especificación del lenguaje. El compilador debe emitir ese error y no puede suprimirlo con ningún análisis adicional. – notnoop

+0

@msaeed: Gracias, eso es exactamente lo que sospecharía. –

7

La especificación de idioma de Java requiere que se asigne una variable antes de usarla. El JLS define reglas específicas para las conocidas como reglas de "Asignación Definitiva". Todos los compiladores de Java deben cumplirlos.

JLS 16.2.15:

V se asignaron claramente antes de que el bloque finally si y sólo si V es, sin duda asignados antes de la sentencia try.

En otras palabras, cuando se considera la instrucción finally, las instrucciones try y catch block dentro de una declaración try-catch-finally no se consideran.

No hace falta decir que esa especificación es muy conservadora aquí, pero preferirían tener la especificación simple aunque un poco limitada (creo que las reglas ya son complicadas) que ser indulgente pero difícil de entender y razonar.

Los compiladores tienen que seguir estas reglas de Asignación Definitiva, por lo que todos los compiladores emiten los mismos errores. Los compiladores no pueden realizar ningún análisis adicional que el JLS especifica para suprimir cualquier error.

+0

+1 @msaeed, ¡buen descubrimiento! –

1

Supongo que Java Compiler asume el peor de los casos: no hay garantía de que se ejecute algo en el bloque try por algún motivo. Entonces su queja es válida. La variable podría no haberse inicializado.

+0

Por lo que he aprendido estudiando para Sun SCJP hasta ahora, el compilador tiene un problema con una variable cuando se asignó en un bloque de código condicional. El código funciona bien sin el fin, por lo que asume que el bloqueo try ocurrirá. Creo que dado que todas las excepciones ocurren en tiempo de ejecución, no son la preocupación del compilador – Adam

2

errores de compilación se producen en bloques condicionales si el compilador no es seguro que el siguiente (éxito) declaración será dirigido como

int i=5;int d; 
if(i<10) 
{system.out.println(d);} 

no se produzcan errores de compilación si la declaración condicional que es seguro y indudable código no ser alcanzado como

int i; 

if(true){}

else 
{System.out.println(d);} 

y compilador se producirán errores si definitivamente ocurrirá la sentencia condicional y el código indudable se alcanzará como

int i; 
if(true) 
{System.out.println(d);} 
else{} 

como tratar de bloques que entran en esta pincha.

Cuestiones relacionadas