En el ámbito del método o de la clase, la línea siguiente se compila (con advertencia):
int x = x = 1;
En el ámbito de la clase, donde las variables obtienen sus valores predeterminados , lo siguiente da un error de 'referencia indefinida':
int x = x + 1;
¿No es el primero que x = x = 1
debería terminar con el mismo error de 'referencia indefinida'? ¿O tal vez int x = x + 1
debería compilarse la segunda línea ? ¿O hay algo que me estoy perdiendo?
java
compiler-construction
Marcin
fuente
fuente
static
en la variable de alcance de clase, como enstatic int x = x + 1;
, ¿obtendrá el mismo error? Porque en C # marca la diferencia si es estático o no estático.static int x = x + 1
falla en Java.int a = this.a + 1;
yint b = 1; int a = b + 1;
en ámbito de clase (ambos de los cuales están bien en Java) fallar, probablemente debido a §17.4.5.2 - "Una variable de inicialización para un campo de instancia no se puede hacer referencia a la instancia que se está creando." No sé si está explícitamente permitido en algún lugar, pero la estática no tiene tal restricción. En Java, las reglas son diferentes ystatic int x = x + 1
fallan por la misma razón que loint x = x + 1
haceRespuestas:
tl; dr
Para los campos ,
int b = b + 1
es ilegal porqueb
es una referencia hacia adelante ilegal ab
. De hecho, puede solucionar esto escribiendoint b = this.b + 1
, que se compila sin quejas.Para las variables locales ,
int d = d + 1
es ilegal porqued
no se inicializa antes de su uso. Este no es el caso de los campos, que siempre se inicializan por defecto.Puede ver la diferencia al intentar compilar
int x = (x = 1) + x;
como una declaración de campo y como una declaración de variable local. El primero fallará, pero el segundo tendrá éxito debido a la diferencia de semántica.
Introducción
En primer lugar, las reglas para los inicializadores de variables locales y de campo son muy diferentes. Entonces, esta respuesta abordará las reglas en dos partes.
Usaremos este programa de prueba en todo momento:
La declaración de
b
no es válida y falla con unillegal forward reference
error.La declaración de
d
no es válida y falla con unvariable d might not have been initialized
error.El hecho de que estos errores sean diferentes debería indicar que las razones de los errores también son diferentes.
Campos
Los inicializadores de campo en Java se rigen por JLS §8.3.2 , Inicialización de campos.
El alcance de un campo se define en JLS §6.3 , Alcance de una declaración.
Las reglas relevantes son:
m
declarado o heredado por un tipo de clase C (§8.1.6) es el cuerpo completo de C, incluidas las declaraciones de tipo anidado.§8.3.2.3 dice:
De hecho, puede hacer referencia a los campos antes de que se hayan declarado, excepto en ciertos casos. Estas restricciones están destinadas a evitar códigos como
de la compilación. La especificación de Java dice que "las restricciones anteriores están diseñadas para detectar, en tiempo de compilación, inicializaciones circulares o con formato incorrecto".
¿A qué se reducen realmente estas reglas?
En resumen, las reglas básicamente dicen que debe declarar un campo antes de una referencia a ese campo si (a) la referencia está en un inicializador, (b) la referencia no se está asignando, (c) la referencia es un nombre simple (sin calificadores como
this.
) y (d) no se está accediendo desde dentro de una clase interna. Por tanto, una referencia directa que satisfaga las cuatro condiciones es ilegal, pero una referencia directa que falla en al menos una condición está bien.int a = a = 1;
compila porque viola (b): la referencia a la quea
se está asignando, por lo que es legal hacer referenciaa
antes dea
la declaración completa de '.int b = this.b + 1
también compila porque viola (c): la referenciathis.b
no es un nombre simple (está calificado conthis.
). Esta extraña construcción todavía está perfectamente bien definida, porquethis.b
tiene el valor cero.Entonces, básicamente, las restricciones en las referencias de campo dentro de los inicializadores impiden que
int a = a + 1
se compile correctamente.Observe que la declaración de campo
int b = (b = 1) + b
se fallan para compilar, porque la finalb
es todavía una referencia ilegal hacia adelante.Variables locales
Las declaraciones de variables locales se rigen por JLS §14.4 , Declaraciones de declaración de variables locales.
El alcance de una variable local se define en JLS §6.3 , Alcance de una declaración:
Tenga en cuenta que los inicializadores están dentro del alcance de la variable que se declara. Entonces, ¿por qué no se
int d = d + 1;
compila?La razón se debe a la regla de Java sobre la asignación definitiva ( JLS §16 ). La asignación definida básicamente dice que cada acceso a una variable local debe tener una asignación anterior a esa variable, y el compilador de Java verifica los bucles y las ramas para garantizar que la asignación siempre ocurra antes de cualquier uso (esta es la razón por la que la asignación definitiva tiene una sección de especificación completa dedicada lo). La regla básica es:
x
,x
debe asignarse definitivamente antes del acceso, o se producirá un error en tiempo de compilación.En
int d = d + 1;
, el acceso ad
se resuelve bien a la variable local, pero comod
no se ha asignado antesd
se accede, el compilador emite un error. Enint c = c = 1
,c = 1
ocurre primero, que asignac
, y luegoc
se inicializa con el resultado de esa asignación (que es 1).Tenga en cuenta que debido a las reglas de asignación definidas, la declaración de la variable local
int d = (d = 1) + d;
se compilará correctamente (a diferencia de la declaración de campoint b = (b = 1) + b
), porqued
definitivamente se asigna cuandod
se alcanza la final .fuente
int b = b + 1
b está a la derecha (no a la izquierda) de la asignación, por lo que violaría esto ...int x = x = 1
, en la que caso de que nada de esto se aplicaría.es equivalente a
mientras en
primero tenemos que calcular,
x+1
pero el valor de x no se conoce, por lo que obtiene un error (el compilador sabe que el valor de x no se conoce)fuente
int x = x = 1;
es equivalente aint x = (x = 1)
, nox = 1; x = x;
. No debería recibir una advertencia del compilador por hacer esto.int x = x = 1;
s equivalente a intx = (x = 1)
debido a la asociatividad derecha del=
operadorint x = (x = 1)
es equivalente aint x; x = 1; x = x;
(declaración de variable, evaluación del inicializador de campo, asignación de variable al resultado de dicha evaluación), de ahí la advertenciaEs aproximadamente equivalente a:
En primer lugar,
int <var> = <expression>;
siempre equivale aEn este caso, su expresión es
x = 1
, que también es una declaración.x = 1
es una declaración válida, ya que la varx
ya ha sido declarada. También es una expresión con el valor 1, que luego se asigna dex
nuevo.fuente
0
valor predeterminado para ints, por lo que esperaría que el resultado sea 1, noundefined reference
.x + 1
no tiene un valor definido, porquex
no está inicializado.x
se define como una variable miembro ("en el ámbito de la clase").En Java o en cualquier lenguaje moderno, la asignación proviene de la derecha.
Suponga que si tiene dos variables xey,
Esta declaración es válida y así es como el compilador los divide.
Pero en tu caso
El compilador dio una excepción porque se divide así.
fuente
int x = x = 1;
no es igual a:javap nos ayuda nuevamente, estas son instrucciones JVM generadas para este código:
más como:
Aquí no hay razón para arrojar un error de referencia indefinido. Ahora se usa la variable antes de su inicialización, por lo que este código cumple completamente con la especificación. De hecho, no hay uso de variable en absoluto , solo asignaciones. Y el compilador JIT irá aún más lejos, eliminará tales construcciones. Honestamente, no entiendo cómo este código está conectado con la especificación de JLS de inicialización y uso de variables. Sin uso, sin problemas. ;)
Por favor corrija si me equivoco. No puedo entender por qué otras respuestas, que se refieren a muchos párrafos de JLS, recopilan tantas ventajas. Estos párrafos no tienen nada en común con este caso. Solo dos asignaciones en serie y nada más.
Si escribimos:
es igual a:
La mayoría de las expresiones a la derecha se asignan a las variables una por una, sin recursividad. Podemos alterar las variables de la forma que queramos:
fuente
En
int x = x + 1;
agrega 1 a X, por lo que lo que es el valor dex
, no es creado todavía.Pero en
int x=x=1;
se compilará sin error porque asigna 1 ax
.fuente
Su primer fragmento de código contiene un segundo en
=
lugar de un más. Esto se compilará en cualquier lugar, mientras que la segunda parte del código no se compilará en ningún lugar.fuente
En la segunda parte del código, x se usa antes de su declaración, mientras que en la primera parte del código solo se asigna dos veces, lo que no tiene sentido pero es válido.
fuente
Vamos a desglosarlo paso a paso, asociativo a la derecha
x = 1
, asignar 1 a una variable xint x = x
, asigne lo que x es a sí mismo, como un int. Dado que x se asignó previamente como 1, conserva 1, aunque de forma redundante.Eso se compila bien.
x + 1
, agregue uno a una variable x. Sin embargo, si x no está definido, se producirá un error de compilación.int x = x + 1
, por lo tanto, esta línea compila errores ya que la parte derecha de los iguales no compilará agregando uno a una variable no asignadafuente
=
operadores, por lo que es lo mismo queint x = (x = 1);
.El segundo
int x=x=1
es compilar porque está asignando el valor a la x, pero en otro casoint x=x+1
aquí la variable x no está inicializada, recuerde que en Java la variable local no está inicializada al valor predeterminado. Nota Si está (int x=x+1
) en el alcance de la clase también, también dará un error de compilación ya que la variable no se crea.fuente
se compila correctamente en Visual Studio 2008 con advertencia
fuente
c
lugar de,java
pero aparentemente fue la otra pregunta.bool y;
yy==true
devolverá falso.void main() { int x = x + 1; printf("%d ", x); }
en Visual Studio 2008, en Debug obtengo la excepciónRun-Time Check Failure #3 - The variable 'x' is being used without being initialized.
y en Release obtengo el número1896199921
impreso en la consola.static
campo (variable estática de nivel de clase), se aplican las mismas reglas. Por ejemplo, un campo declarado comopublic static int x = x + 1;
compila sin advertencia en Visual C #. ¿Posiblemente lo mismo en Java?x no se inicializa en
x = x + 1
;.El lenguaje de programación Java tiene un tipo estático, lo que significa que todas las variables deben declararse primero antes de que puedan usarse.
Ver tipos de datos primitivos
fuente
La línea de código no se compila con una advertencia debido a cómo funciona realmente el código. Cuando ejecuta el código
int x = x = 1
, Java primero crea la variablex
, tal como se define. Luego ejecuta el código de asignación (x = 1
). Comox
ya está definido, el sistema no tiene errores configuradosx
en 1. Esto devuelve el valor 1, porque ahora es el valor dex
. Por lo tanto,x
ahora finalmente se establece como 1.Java básicamente ejecuta el código como si fuera esto:
Sin embargo, en su segundo fragmento de código,
int x = x + 1
la+ 1
declaraciónx
debe definirse, lo que para entonces no es así. Dado que las declaraciones de asignación siempre significan que el código a la derecha de=
se ejecuta primero, el código fallará porque nox
está definido. Java ejecutaría el código así:fuente
Cumplier leyó declaraciones de derecha a izquierda y diseñamos para hacer lo contrario. Por eso le molestó al principio. Haga de esto un hábito para leer declaraciones (código) de derecha a izquierda, no tendrá ese problema.
fuente