Permítanme decir esto muy claramente, porque la gente lo entiende mal todo el tiempo:
El orden de evaluación de las subexpresiones es independiente tanto de la asociatividad como de la precedencia . La asociatividad y la precedencia determinan en qué orden se ejecutan los operadores , pero no determinan en qué orden se evalúan las subexpresiones . Su pregunta es sobre el orden en que se evalúan las subexpresiones .
Considere A() + B() + C() * D()
. La multiplicación tiene mayor precedencia que la suma, y la suma es asociativa por la izquierda, por lo que esto es equivalente a (A() + B()) + (C() * D())
Pero saber eso solo te dice que la primera suma ocurrirá antes de la segunda suma, y que la multiplicación ocurrirá antes de la segunda suma. ¡No le dice en qué orden se llamarán A (), B (), C () y D ()! (Tampoco le dice si la multiplicación ocurre antes o después de la primera adición). Sería perfectamente posible obedecer las reglas de precedencia y asociatividad compilando esto como:
d = D()
b = B()
c = C()
a = A()
sum = a + b
product = c * d
result = sum + product
Allí se siguen todas las reglas de precedencia y asociatividad: la primera adición ocurre antes de la segunda adición y la multiplicación ocurre antes de la segunda adición. ¡Claramente podemos hacer las llamadas a A (), B (), C () y D () en cualquier orden y aún obedecer las reglas de precedencia y asociatividad!
Necesitamos una regla no relacionada con las reglas de precedencia y asociatividad para explicar el orden en que se evalúan las subexpresiones. La regla relevante en Java (y C #) es "las subexpresiones se evalúan de izquierda a derecha". Dado que A () aparece a la izquierda de C (), A () se evalúa primero, independientemente del hecho de que C () esté involucrado en una multiplicación y A () esté involucrado solo en una suma.
Entonces ahora tiene suficiente información para responder su pregunta. En a[b] = b = 0
las reglas de asociatividad decimos que esto es a[b] = (b = 0);
pero eso no significa que el b=0
primero corre! Las reglas de precedencia dicen que la indexación tiene una precedencia mayor que la asignación, pero eso no significa que el indexador se ejecute antes de la asignación de más a la derecha .
(ACTUALIZACIÓN: una versión anterior de esta respuesta tenía algunas omisiones pequeñas y prácticamente sin importancia en la sección que sigue que he corregido. También escribí un artículo de blog que describe por qué estas reglas son sensatas en Java y C # aquí: https: // ericlippert.com/2019/01/18/indexer-error-cases/ )
La precedencia y la asociatividad solo nos dicen que la asignación de cero a b
debe ocurrir antes de la asignación de a[b]
, porque la asignación de cero calcula el valor que se asigna en la operación de indexación. Precedencia y asociatividad solos no dicen nada sobre si el a[b]
se evalúa antes o después de la b=0
.
Nuevamente, esto es lo mismo que: A()[B()] = C()
- Todo lo que sabemos es que la indexación debe realizarse antes de la asignación. No sabemos si A (), B () o C () se ejecuta primero en función de la precedencia y la asociatividad . Necesitamos otra regla que nos diga eso.
La regla es, nuevamente, "cuando tenga la opción de qué hacer primero, vaya siempre de izquierda a derecha". Sin embargo, hay una arruga interesante en este escenario específico. ¿El efecto secundario de una excepción lanzada causado por una colección nula o un índice fuera de rango se considera parte del cálculo del lado izquierdo de la asignación o parte del cálculo de la asignación en sí? Java elige este último. (Por supuesto, esta es una distinción que solo importa si el código ya es incorrecto , porque el código correcto no elimina la referencia nula ni pasa un índice incorrecto en primer lugar).
¿Así que lo que pasa?
- El
a[b]
está a la izquierda de b=0
, por lo que se a[b]
ejecuta primero , lo que da como resultado a[1]
. Sin embargo, la verificación de la validez de esta operación de indexación se retrasa.
- Entonces
b=0
sucede.
- Entonces ocurre la verificación que
a
es válida y a[1]
está dentro del rango
- La asignación del valor
a[1]
ocurre en último lugar.
Entonces, aunque en este caso específico hay algunas sutilezas a considerar para esos raros casos de error que no deberían ocurrir en el código correcto en primer lugar, en general puede razonar: las cosas de la izquierda suceden antes que las de la derecha . Esa es la regla que estás buscando. Hablar de precedencia y asociatividad es confuso e irrelevante.
La gente se equivoca todo el tiempo en estas cosas , incluso las que deberían saberlo mejor. He editado demasiados libros de programación que establecían las reglas incorrectamente, por lo que no es de extrañar que mucha gente tenga creencias completamente incorrectas sobre la relación entre precedencia / asociatividad y orden de evaluación, es decir, que en realidad no existe tal relación ; son independientes.
Si este tema le interesa, vea mis artículos sobre el tema para leer más:
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
Se trata de C #, pero la mayoría de estas cosas se aplican igualmente bien a Java.