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 ax
", es razonable que la expresiónfoo.x
tenga un significado diferente; es decir, cada parte de la expresión es válida y accede ax
.
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
.