Deixe-me dizer isso muito claramente, porque as pessoas não entendem isso o tempo todo:
A ordem de avaliação das subexpressões é independente da associatividade e da precedência . A associatividade e a precedência determinam em que ordem os operadores são executados, mas não determinam em que ordem as subexpressões são avaliadas. Sua pergunta é sobre a ordem em que as subexpressões são avaliadas.
Considere A() + B() + C() * D()
. A multiplicação tem precedência mais alta do que a adição, e a adição é associativa à esquerda, então isso é equivalente a (A() + B()) + (C() * D())
Mas saber isso apenas informa que a primeira adição acontecerá antes da segunda adição e que a multiplicação acontecerá antes da segunda adição. Ele não diz em que ordem A (), B (), C () e D () serão chamados! (Também não informa se a multiplicação acontece antes ou depois da primeira adição.) Seria perfeitamente possível obedecer às regras de precedência e associatividade compilando isso como:
d = D() // these four computations can happen in any order
b = B()
c = C()
a = A()
sum = a + b // these two computations can happen in any order
product = c * d
result = sum + product // this has to happen last
Todas as regras de precedência e associatividade são seguidas lá - a primeira adição acontece antes da segunda adição, e a multiplicação acontece antes da segunda adição. É claro que podemos fazer as chamadas para A (), B (), C () e D () em qualquer ordem e ainda obedecer às regras de precedência e associatividade!
Precisamos de uma regra não relacionada às regras de precedência e associatividade para explicar a ordem em que as subexpressões são avaliadas. A regra relevante em Java (e C #) é "subexpressões são avaliadas da esquerda para a direita". Visto que A () aparece à esquerda de C (), A () é avaliado primeiro, independentemente do fato de que C () está envolvido em uma multiplicação e A () está envolvido apenas em uma adição.
Agora você tem informações suficientes para responder à sua pergunta. Nas a[b] = b = 0
regras de associatividade, diga que isso é, a[b] = (b = 0);
mas isso não significa que o b=0
primeiro é executado! As regras de precedência dizem que a indexação é uma precedência mais alta do que a atribuição, mas isso não significa que o indexador é executado antes da atribuição mais à direita .
(ATUALIZAÇÃO: uma versão anterior desta resposta tinha algumas omissões pequenas e praticamente sem importância na seção a seguir que eu corrigi. Também escrevi um artigo de blog descrevendo por que essas regras são razoáveis em Java e C # aqui: https: // ericlippert.com/2019/01/18/indexer-error-cases/ )
A precedência e a associatividade apenas nos dizem que a atribuição de zero a b
deve acontecer antes da atribuição a a[b]
, porque a atribuição de zero calcula o valor atribuído na operação de indexação. A precedência e a associatividade sozinhas não dizem nada sobre se o a[b]
é avaliado antes ou depois do b=0
.
Novamente, isso é exatamente o mesmo que: A()[B()] = C()
- Tudo o que sabemos é que a indexação deve acontecer antes da atribuição. Não sabemos se A (), B () ou C () é executado primeiro com base na precedência e na associatividade . Precisamos de outra regra para nos dizer isso.
A regra é, novamente, "quando você tiver uma escolha sobre o que fazer primeiro, vá sempre da esquerda para a direita". No entanto, há uma ruga interessante neste cenário específico. O efeito colateral de uma exceção lançada é causado por uma coleção nula ou índice fora do intervalo considerado parte do cálculo do lado esquerdo da atribuição ou parte do cálculo da própria atribuição? Java escolhe o último. (Claro, esta é uma distinção que só importa se o código já estiver errado , porque o código correto não cancela a referência de nulo ou passa um índice incorreto em primeiro lugar.)
Então o que acontece?
- O
a[b]
está à esquerda do b=0
, portanto, o a[b]
é executado primeiro , resultando em a[1]
. No entanto, a verificação da validade desta operação de indexação está atrasada.
- Então
b=0
acontece.
- Então, a verificação que
a
é válida e a[1]
está dentro do intervalo acontece
- A atribuição do valor a
a[1]
acontece por último.
Portanto, embora neste caso específico haja algumas sutilezas a serem consideradas para aqueles raros casos de erro que não deveriam estar ocorrendo no código correto em primeiro lugar, em geral você pode raciocinar: as coisas à esquerda acontecem antes das coisas à direita . Essa é a regra que você está procurando. Falar de precedência e associatividade é confuso e irrelevante.
As pessoas entendem isso errado o tempo todo , até mesmo pessoas que deveriam saber disso. Eu editei muitos livros de programação que declaravam as regras incorretamente, então não é surpresa que muitas pessoas tenham crenças completamente incorretas sobre a relação entre precedência / associatividade e ordem de avaliação - ou seja, que na realidade não existe tal relação ; eles são independentes.
Se este tópico interessar a você, veja meus artigos sobre o assunto para leitura adicional:
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
Eles são sobre C #, mas a maioria dessas coisas se aplica igualmente bem ao Java.