Asignando una variable, lo que realmente sucede, Java

En el siguiente ejemplo, ¿qué sucede realmente?

int a = 1;
a += (a = 2);

La salida es 3, sin embargo, quería saber qué sucede realmente debajo de las sábanas. Por ejemplo, sé que los paréntesis tienen mayor prioridad que + así que pasando primero (a = 2) la expresión debería convertirse en a = 2 + 2. En tiempo de ejecución, primero se debe ejecutar la expresión entre paréntesis y luego a se convierte en 2. Parece que la primera a a la izquierda para + se "carga" antes de (a = 2) y esta última expresión no parece anular la carga anterior. En otras palabras, estoy bastante confundido con lo que sucede exactamente detrás de escena.

Si alguien sabe, muchas gracias de antemano.

preguntado el 08 de marzo de 13 a las 21:03

Igual que int a = 1; int tmpvar = (a = 2); a += tmpvar; -

se traduce en a = a + (a = 2);, y los operandos se evalúan de izquierda a derecha. -

Por cierto, es un delito de programación capital usar la asignación en medio de una declaración como esa. -

Los paréntesis no se ejecutan. O evaluado. O algo. Simplemente eliminan la ambigüedad de la asociación. -

3 Respuestas

De la JLS sección §15.26.2 Operadores de asignación compuesta:

Una expresión de asignación compuesta de la forma E1 op= E2 es equivalente a E1 = (T)((E1) op (E2)), donde T es el tipo de E1, excepto que E1 se evalúa solo una vez.

Así que para tu ejemplo tenemos:

a = (a) + (a = 2)

Con la expresión evaluada de izquierda a derecha. Por lo tanto, la salida de 3

respondido 08 mar '13, 21:03

La declaración que citó no dice nada sobre el orden de evaluación. - nhahtdh

@ rich.okelly ok, ¿entonces básicamente la izquierda (a) se pone a su vez entre paréntesis implícitamente? - Rollerball

@nhahtdh Se infiere: el orden de precedencia está garantizado por el orden en la declaración con corchetes que eliminan la ambigüedad. - Rich O'Kelly

@Rollerball Sí, pero no afecta el resultado de la declaración. Los corchetes allí son redundantes. El lado derecho de la = el operador se evalúa de izquierda a derecha - Rich O'Kelly

@rich.okelly: Según su línea de razonamiento, es mejor decir que el orden está garantizado por el hecho de que todos los op se evalúan de izquierda a derecha. - nhahtdh

Ver el ejemplo referenciado 15.7.1-2 en http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.1, que es casi idéntico al ejemplo que proporcionaste. En particular:

Si el operador es un operador de asignación compuesta (§15.26.2), la evaluación del operando de la izquierda incluye recordar la variable que denota el operando de la izquierda y obtener y guardar el valor de esa variable para usar en la operación binaria implícita. .

Debido a esta precedencia, la mano izquierda de += se evalúa primero.

Puede resultarle confuso debido a los paréntesis, pero tenga en cuenta la sección sobre la evaluación de paréntesis: http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.3, y en particular:

El lenguaje de programación Java respeta el orden de evaluación indicado explícitamente entre paréntesis e implícitamente por precedencia de operadores.

En este caso, la precedencia implícita establecida por el operador += indica que el operando de la izquierda se recordará según la especificación. Si bien es cierto que los operadores de asignación, incluido "+=", tienen la prioridad más baja, la especificación para += indica que el operando de la izquierda se recordará según 15.26.2.

respondido 08 mar '13, 21:03

en realidad, la prioridad de += es la menor y debe evaluarse en último lugar - Rollerball

Por favor, lea el texto del ejemplo: En el siguiente programa, las dos instrucciones de asignación obtienen y recuerdan el valor del operando de la izquierda, que es 9, antes de que se evalúe el operando de la derecha del operador de suma, momento en el cual la variable se establece en 3. - Kirby

Este ejemplo es el mismo que el tuyo, pero con números diferentes. - Kirby

Echemos un vistazo al bytecode del siguiente programa:

package A;

public class Test
{
    public static void main(String[] args)
    {
        int a = 1;
        a += (a = 2);
    }
}

Solo necesitamos ejecutar este comando:

javap -c Test.class

para obtener el siguiente código de bytes:

public class A.Test {
  public A.Test();
    Code:
       0: aload_0
       1: invokespecial #1           // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iload_1
       3: iconst_2
       4: dup
       5: istore_1
       6: iadd
       7: istore_1
       8: return
}

Explicación:

Solo nos centraremos en las dos líneas dentro del método principal:

int a = 1;
a += (a = 2);

[int a = 1; comienza aquí]

0: iconst_1
  • Empuja int 1 en la pila.
-------------
|           |
-------------
|           |
-------------
|     1     |
-------------
    STACK

1: istore_1
  • Extrae el valor int de la pila para variable 1 (variable 1 representa a)
-------------
|           |             variable 1
-------------           --------------
|           |           |     1      |
-------------           --------------
|           |
-------------
    STACK

[int a = 1; termina aquí]


[a += (a = 2); comienza aquí]

2: iload_1
  • Carga un valor int de local variable 1 y lo empuja hacia la pila.
-------------
|           |             variable 1
-------------           --------------
|           |           |            |
-------------           --------------
|     1     |
-------------
    STACK

3: iconst_2
  • Empuja int 2 en la pila.
-------------
|           |             variable 1
-------------           --------------
|     2     |           |            |
-------------           --------------
|     1     |
-------------
    STACK

4: dup
  • duplicar el valor en la parte superior de la pila.
-------------
|     2     |             variable 1
-------------           --------------
|     2     |           |            |
-------------           --------------
|     1     |
-------------
    STACK

5: istore_1
  • Extrae el valor int de la pila para variable 1.
-------------
|           |             variable 1
-------------           --------------
|     2     |           |      2     |
-------------           --------------
|     1     |
-------------
    STACK

6: iadd
  • Suma los dos valores superiores juntos.
-------------
|           |             variable 1
-------------           --------------
|           |           |      2     |
-------------           --------------
|     3     |
-------------
    STACK

7: istore_1
  • Extrae el valor int de la pila para variable 1.
-------------
|           |             variable 1
-------------           --------------
|           |           |      3     |
-------------           --------------
|           |
-------------
    STACK

[a += (a = 2); termina aquí]


8: return
  • El método principal regresa.

Conclusión:

a = a + (a = 2) se realiza a través de varias operaciones. 2: iload_1 se ejecuta como primer comando de a += (a = 2); que lee el primer operando de la ecuación a = a + (a = 2) y empuja sobre la pila.

Siguiente, 3: iconst_2 y 4: dup se ejecutan que básicamente empujan int 2 dos veces en la pila; uno para cargarlo a y el otro como segundo operando. Después, 5: istore_1 se ejecuta que se está cargando 2 dentro a (a = 2).

Finalmente, 6: iadd y 7: istore_1 se ejecutan donde 6: iadd suma el primer operando y el segundo operando y empuja el resultado a la pila, y 7: istore_1 muestra el resultado y lo carga en a.


Para simplificar, echemos un vistazo rápido a este código:

int a = 1;
int b = 3;
a += b;

y aquí está su código de bytes:

public class A.Test {
  public A.Test();
    Code:
       0: aload_0
       1: invokespecial #1            // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_3
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: istore_1
       8: return
}

Como puede ver, simplemente hace lo siguiente:

  • Cargas internas 1 dentro a.
  • Cargas internas 3 dentro b.
  • Empuja a luego b en la pila.
  • Realiza la suma sobre ellos y empuja el resultado a la pila.
  • Extrae el resultado de la pila y lo almacena en a.

respondido 08 mar '13, 22:03

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.