java static initialization local-variables jls

java - ¿Por qué no podemos usar una variable local no inicializada para acceder al contenido estático desde el tipo de esa variable?



static initialization (4)

Capítulo 16. Asignación definida

Cada variable local (§14.4) y cada campo final en blanco (§4.12.4, §8.3.1.2) deben tener un valor definitivamente asignado cuando se produce cualquier acceso a su valor.

Realmente no importa a qué intente acceder a través de una variable local. La regla es que definitivamente debe asignarse antes de eso.

Para evaluar una expresión de acceso de campo foo.x , la parte primary de ella ( foo ) debe evaluarse primero. Significa que se producirá acceso a foo , lo que dará como resultado un error en tiempo de compilación.

Para cada acceso de una variable local o campo final en blanco x, x debe asignarse definitivamente antes del acceso, o se produce un error en tiempo de compilación.

Al intentar acceder al campo estático x través de una variable local no inicializada Foo foo; foo.x me sale el error de compilación La Variable ''foo'' might not have been initialized .

class Foo{ public static int x = 1; public static void main(String[] args) { Foo foo; System.out.println(foo.x); // Error: Variable ''foo'' might not have been initialized } }

Podría parecer que este error tiene sentido, pero solo hasta que nos demos cuenta de que para acceder a un miembro static , el compilador en realidad no usa el valor de una variable, sino solo su tipo .

Por ejemplo, puedo inicializar foo con un valor null y esto nos permitirá acceder a x sin ningún problema:

Foo foo = null; System.out.println(foo.x); //compiles and while running prints 1!!!

Tal escenario funciona porque el compilador se da cuenta de que x es estático y trata a foo.x como se escribió como Foo.x (al menos eso es lo que pensé hasta ahora).

Entonces, ¿por qué el compilador insiste repentinamente en que tenga un valor que NO usará en ese lugar?

(Descargo de responsabilidad: este no es un código que se usaría en una aplicación real, solo un fenómeno interesante al que no pude encontrar respuesta en Stack Overflow, así que decidí preguntar al respecto).


Es valioso mantener las reglas lo más simple posible, y "no usar una variable que no haya sido inicializada" es tan simple como parece.

Más concretamente, existe una forma establecida de llamar a métodos estáticos: utilice siempre el nombre de la clase, no una variable.

System.out.println(Foo.x);

La variable "foo" es una sobrecarga no deseada que debe eliminarse, y los errores y advertencias del compilador podrían verse como una ayuda para lograrlo.


Otras respuestas explican perfectamente el mecanismo detrás de lo que está sucediendo. Tal vez también quería la razón detrás de la especificación de Java. Al no ser un experto en Java, no puedo darle las razones originales, pero permítame señalar esto:

  • Cada parte del código tiene un significado o desencadena un error de compilación.
  • (Para las estadísticas, porque una instancia es innecesaria, Foo.x es natural).
  • Ahora, ¿qué haremos con foo.x (acceso a través de la variable de instancia)?
    • Podría ser un error de compilación, como en C #, o
    • Tiene un significado. Como Foo.x ya significa "simplemente acceder a x ", es razonable que la expresión foo.x tenga un significado diferente; es decir, cada parte de la expresión es válida y accede a x .

Esperemos que alguien con conocimiento pueda decir la verdadera razón. :-)


§15.11. Expresiones de acceso de campo :

Si el campo es estático :

La expresión primaria se evalúa y el resultado se descarta . Si la evaluación de la expresión Primaria se completa abruptamente, la expresión de acceso de campo se completa abruptamente por la misma razón.

Donde antes indica que el acceso de campo se identifica mediante Primary.Identifier .

Esto muestra que, aunque parece no utilizar el Primary , aún se evalúa y el resultado se descarta, por lo que deberá inicializarse. Esto puede marcar la diferencia cuando la evaluación detiene el acceso como se indica en la cita.

EDITAR:

Aquí hay un breve ejemplo solo para demostrar visualmente que el Primary se evalúa a pesar de que se descarta el resultado:

class Foo { public static int x = 1; public static Foo dummyFoo() throws InterruptedException { Thread.sleep(5000); return null; } public static void main(String[] args) throws InterruptedException { System.out.println(dummyFoo().x); System.out.println(Foo.x); } }

Aquí puede ver que dummyFoo() todavía se evalúa porque la print se retrasa por 5 segundos Thread.sleep() aunque siempre devuelve un valor null que se descarta.

Si no se evaluó la expresión, la print aparecería instantáneamente, lo que se puede ver cuando la clase Foo se usa directamente para acceder a x con Foo.x

Nota: La invocación del método también se considera una Primary muestra en §15.8 Expresiones primarias .