¿Por qué las variables locales no se inicializan en Java?

Resuelto Shivasubramanian A asked hace 15 años • 0 respuestas

¿Hubo alguna razón por la cual los diseñadores de Java sintieron que a las variables locales no se les debía dar un valor predeterminado? En serio, si a las variables de instancia se les puede dar un valor predeterminado, ¿por qué no podemos hacer lo mismo con las variables locales?

Y también genera problemas, como se explica en este comentario de una publicación de blog :

Bueno, esta regla es más frustrante cuando se intenta cerrar un recurso en un bloque finalmente. Si creo una instancia del recurso dentro de un intento, pero intento cerrarlo finalmente, aparece este error. Si muevo la creación de instancias fuera del intento, aparece otro error que indica que debe estar dentro de un intento.

Muy frustrante.

Shivasubramanian A avatar Jan 06 '09 14:01 Shivasubramanian A
Aceptado

Las variables locales se declaran principalmente para realizar algún cálculo. Por lo tanto, es decisión del programador establecer el valor de la variable y no debe tomar un valor predeterminado.

Si el programador, por error, no inicializó una variable local y toma un valor predeterminado, entonces el resultado podría ser algún valor inesperado. Entonces, en el caso de variables locales, el compilador le pedirá al programador que lo inicialice con algún valor antes de acceder a la variable para evitar el uso de valores indefinidos.

Warrior avatar Jan 06 '2009 07:01 Warrior

El "problema" al que vincula parece describir esta situación:

SomeObject so;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  so.CleanUp(); // Compiler error here
}

La queja del comentarista es que el compilador se opone a la línea de la finallysección, alegando que sopodría no estar inicializada. Luego, el comentario menciona otra forma de escribir el código, probablemente algo como esto:

// Do some work here ...
SomeObject so = new SomeObject();
try {
  so.DoUsefulThings();
} finally {
  so.CleanUp();
}

El comentarista no está satisfecho con esa solución porque el compilador dice que el código "debe estar dentro de un intento". Supongo que eso significa que parte del código puede generar una excepción que ya no se maneja. No estoy seguro. Ninguna versión de mi código maneja excepciones, por lo que cualquier cosa relacionada con excepciones en la primera versión debería funcionar igual en la segunda.

De todos modos, esta segunda versión del código es la forma correcta de escribirlo. En la primera versión, el mensaje de error del compilador era correcto. Es posible que la sovariable no esté inicializada. En particular, si el SomeObjectconstructor falla, sono se inicializará, por lo que será un error intentar llamarlo so.CleanUp. Ingrese siempre a la trysección después de haber adquirido el recurso que finallyfinaliza la sección.

El bloque try- después de la inicialización está ahí solo para proteger la instancia, para asegurarse de que se limpie sin importar lo que suceda. Si hay otras cosas que deben ejecutarse, pero no están relacionadas con si la instancia fue asignada como propiedad, entonces deberían ir en otro bloque , probablemente uno que englobe el que he mostrado.finallysoSomeObjectSomeObject tryfinally

Exigir que las variables se asignen manualmente antes de su uso no genera problemas reales. Solo genera problemas menores, pero su código será mejor. Tendrá variables con un alcance más limitado y trybloques finallyque no intentan proteger demasiado.

Si las variables locales tuvieran valores predeterminados, soen el primer ejemplo habrían sido null. Eso realmente no habría solucionado nada. En lugar de obtener un error en tiempo de compilación en el finallybloque, tendría un NullPointerExceptionerror al acecho que podría ocultar cualquier otra excepción que pudiera ocurrir en la sección "Trabajar aquí" del código. (¿O las excepciones en finallylas secciones se encadenan automáticamente a la excepción anterior? No lo recuerdo. Aun así, tendrías una excepción adicional similar a la real).

Rob Kennedy avatar Jan 06 '2009 08:01 Rob Kennedy

Además, en el siguiente ejemplo, es posible que se haya producido una excepción dentro de la construcción SomeObject, en cuyo caso la variable 'so' sería nula y la llamada a CleanUp generará una excepción NullPointerException.

SomeObject so;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  so.CleanUp(); // Compiler error here
}

Lo que suelo hacer es esto:

SomeObject so = null;
try {
  // Do some work here ...
  so = new SomeObject();
  so.DoUsefulThings();
} finally {
  if (so != null) {
     so.CleanUp(); // safe
  }
}
Electric Monk avatar Jan 06 '2009 09:01 Electric Monk

La respuesta real a su pregunta es porque las variables del método se crean instancias simplemente agregando un número al puntero de la pila. Ponerlos a cero sería un paso adicional. Para las variables de clase, se colocan en la memoria inicializada del montón.

¿Por qué no dar un paso más? Dé un paso atrás: nadie mencionó que la "advertencia" en este caso sea algo muy bueno.

Nunca debes inicializar tu variable a cero o nula en la primera pasada (cuando la codificas por primera vez). Asígnelo al valor real o no lo asigne en absoluto porque si no lo hace, Java puede decirle cuándo realmente comete un error. Tomemos la respuesta de Electric Monk como un gran ejemplo. En el primer caso, en realidad es sorprendentemente útil que te diga que si try() falla porque el constructor de SomeObject lanzó una excepción, entonces terminarías con un NPE finalmente. Si el constructor no puede lanzar una excepción, no debería estar en el intento.

Esta advertencia es un increíble verificador de múltiples rutas para malos programadores que me ha salvado de hacer cosas estúpidas, ya que verifica cada ruta y se asegura de que si usó la variable en alguna ruta, entonces tuvo que inicializarla en cada ruta que conduzca a ella. . Ahora nunca inicializo variables explícitamente hasta que determine que es lo correcto.

Además de eso, ¿no es mejor decir explícitamente "int size=0" en lugar de "int size" y hacer que el próximo programador se dé cuenta de que desea que sea cero?

Por otro lado, no se me ocurre una sola razón válida para que el compilador inicialice todas las variables no inicializadas a 0.

Bill K avatar May 20 '2011 23:05 Bill K