Considere o seguinte exemplo:
class Quirky {
public static void main(String[] args) {
int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false
x = 1; // reset
System.out.println((x = y) == x); // true
}
}
Não tenho certeza se existe um item na Especificação de Linguagem Java que determina o carregamento do valor anterior de uma variável para comparação com o lado direito ( x = y
) que, pela ordem implícita entre colchetes, deve ser calculado primeiro.
Por que a primeira expressão é avaliada como false
, mas a segunda é avaliada como true
? Eu esperava (x = y)
ser avaliado primeiro e, depois, comparar x
-se ( 3
) e retornar true
.
Essa questão é diferente da ordem de avaliação das subexpressões em uma expressão Java , que x
definitivamente não é uma 'subexpressão' aqui. Ele precisa ser carregado para a comparação, em vez de ser 'avaliado'. A pergunta é específica de Java e a expressão x == (x = y)
, ao contrário de construções práticas pouco comuns, geralmente criadas para perguntas complicadas de entrevistas, veio de um projeto real. Era para ser uma substituição de uma linha para o idioma comparar e substituir
int oldX = x;
x = y;
return oldX == y;
que, sendo ainda mais simples que a instrução x86 CMPXCHG, merecia uma expressão mais curta em Java.
fonte
x = y
é certamente relevante e causa o efeito colateralx
definido como o valor dey
.Respostas:
Não. É um equívoco comum que os parênteses tenham algum efeito (geral) na ordem de cálculo ou avaliação. Eles apenas coagem as partes da sua expressão em uma árvore específica, vinculando os operandos certos às operações certas para o trabalho.
(E, se você não usá-los, essas informações são provenientes da "precedência" e associatividade dos operadores, algo que é resultado de como a árvore de sintaxe da linguagem é definida. Na verdade, ainda é exatamente assim que funciona quando você use parênteses, mas simplificamos e dizemos que não confiamos em nenhuma regra de precedência.)
Uma vez feito isso (ou seja, uma vez que seu código foi analisado em um programa), esses operandos ainda precisam ser avaliados e existem regras separadas sobre como isso é feito: as regras ditas (como Andrew nos mostrou) afirmam que o LHS de cada operação é avaliado primeiro em Java.
Observe que esse não é o caso em todos os idiomas; por exemplo, em C ++, a menos que você esteja usando um operador de curto-circuito como
&&
ou||
, a ordem de avaliação dos operandos geralmente não é especificada e você não deve confiar nela de nenhuma maneira.Os professores precisam parar de explicar a precedência do operador usando frases enganosas como "isso faz a adição acontecer primeiro". Dada uma expressão,
x * y + z
a explicação correta seria "a precedência do operador faz a adição acontecer entrex * y
ez
, em vez de entrey
ez
", sem mencionar nenhuma "ordem".fonte
==
é um operador de igualdade binária .fonte
Como LouisWasserman disse, a expressão é avaliada da esquerda para a direita. E o java não se importa com o que "avaliar" realmente faz, apenas se preocupa em gerar um valor (não volátil, final) para trabalhar.
Portanto, para calcular a primeira saída de
System.out.println()
, é feito o seguinte:e para calcular o segundo:
Observe que o segundo valor sempre será avaliado como verdadeiro, independentemente dos valores iniciais de
x
ey
, porque você está comparando efetivamente a atribuição de um valor à variável à qual ele foi atribuídoa = b
eb
será avaliado nessa ordem e sempre será o mesmo por definição.fonte
Há sim. Da próxima vez que você está claro o que a especificação diz, por favor leia a especificação e , em seguida, fazer a pergunta se não está claro.
Essa afirmação é falsa. Parênteses não implicam uma ordem de avaliação . Em Java, a ordem da avaliação é da esquerda para a direita, independentemente dos parênteses. Parênteses determinam onde estão os limites da subexpressão, e não a ordem da avaliação.
A regra para o
==
operador é: avalie o lado esquerdo para produzir um valor, avalie o lado direito para produzir um valor, compare os valores, a comparação é o valor da expressão.Em outras palavras, o significado de
expr1 == expr2
é sempre o mesmo que se você tivesse escritotemp1 = expr1; temp2 = expr2;
e avaliadotemp1 == temp2
.A regra para o
=
operador com uma variável local no lado esquerdo é: avalie o lado esquerdo para produzir uma variável, avalie o lado direito para produzir um valor, execute a atribuição, o resultado é o valor que foi atribuído.Então junte-o:
Temos um operador de comparação. Avalie o lado esquerdo para produzir um valor - obtemos o valor atual de
x
. Avalie o lado direito: é uma atribuição, portanto, avaliamos o lado esquerdo para produzir uma variável - a variávelx
- avaliamos o lado direito - o valor atual dey
- atribua a elax
e o resultado é o valor atribuído. Em seguida, comparamos o valor originalx
com o valor atribuído.Você pode fazer
(x = y) == x
como um exercício. Novamente, lembre-se de que todas as regras para avaliar o lado esquerdo acontecem antes de todas as regras de avaliação do lado direito .Sua expectativa é baseada em um conjunto de crenças incorretas sobre as regras do Java. Espero que agora você tenha crenças corretas e, no futuro, espere coisas verdadeiras.
Esta afirmação é falsa. Essa pergunta é totalmente pertinente.
Esta afirmação também é falsa. É uma subexpressão duas vezes em cada exemplo.
Eu não tenho ideia do que isso significa.
Aparentemente, você ainda tem muitas crenças falsas. Meu conselho é que você leia a especificação até que suas falsas crenças sejam substituídas por verdadeiras.
A proveniência da expressão não é relevante para a questão. As regras para tais expressões estão claramente descritas na especificação; Leia-o!
Como essa substituição de uma linha causou muita confusão em você, leitor do código, eu sugeriria que era uma má escolha. Tornar o código mais conciso, mas mais difícil de entender, não é uma vitória. É improvável que o código seja mais rápido.
Aliás, o C # compara e substitui como um método de biblioteca, que pode ser associado a uma instrução de máquina. Acredito que o Java não possui esse método, pois não pode ser representado no sistema do tipo Java.
fonte
Está relacionado à precedência do operador e como os operadores estão sendo avaliados.
Os parênteses '()' têm maior precedência e associatividade da esquerda para a direita. A igualdade '==' vem em seguida nesta pergunta e tem associatividade da esquerda para a direita. A tarefa '=' vem por último e tem associatividade da direita para a esquerda.
O sistema usa a pilha para avaliar a expressão. A expressão é avaliada da esquerda para a direita.
Agora chega à pergunta original:
Primeiro x (1) será pressionado para empilhar. então interno (x = y) será avaliado e pressionado para empilhar com o valor x (3). Agora x (1) será comparado com x (3), portanto o resultado é falso.
Aqui, (x = y) será avaliado, agora o valor de x se tornará 3 e x (3) será pressionado para empilhar. Agora x (3) com valor alterado após igualdade será empurrado para empilhar. Agora a expressão será avaliada e ambas serão iguais, portanto o resultado é verdadeiro.
fonte
Não é o mesmo. O lado esquerdo sempre será avaliado antes do lado direito, e os colchetes não especificam uma ordem de execução, mas um agrupamento de comandos.
Com:
Você está basicamente fazendo o mesmo que:
E x terá o valor de y após a comparação.
Enquanto estiver com:
Você está basicamente fazendo o mesmo que:
Depois de x assumiu o valor de y . E sempre retornará verdadeiro .
fonte
No primeiro teste que você está verificando, faz 1 == 3.
No segundo teste, sua verificação faz 3 == 3.
(x = y) atribui o valor e esse valor é testado. No exemplo anterior, x = 1 primeiro, depois x é atribuído 3. 1 == 3?
No último, x é atribuído 3 e, obviamente, ainda é 3. Será que 3 == 3?
fonte
Considere este outro exemplo, talvez mais simples:
Aqui, o operador de pré-incremento em
++x
deve ser aplicado antes da comparação, assim como(x = y)
no seu exemplo, deve ser calculado antes da comparação.No entanto, a avaliação da expressão ainda acontece da esquerda para a direita , portanto, a primeira comparação é na verdade
1 == 2
a segunda2 == 2
.O mesmo acontece no seu exemplo.
fonte
As expressões são avaliadas da esquerda para a direita. Nesse caso:
fonte
Basicamente, a primeira instrução x tinha seu valor 1. Então, Java compara 1 == com a nova variável x, que não será a mesma
No segundo, você disse que x = y, o que significa que o valor de x mudou e, portanto, quando você o chamar novamente, será o mesmo valor, portanto, por que é verdadeiro e x == x
fonte
== é um operador de igualdade de comparação e funciona da esquerda para a direita.
aqui o antigo valor atribuído de x é comparado com o novo valor atribuído de x, (1 == 3) // false
Considerando que aqui o novo valor atribuído de x é comparado com o novo valor de retenção de x atribuído a ele imediatamente antes da comparação, (3 == 3) // true
Agora considere isso
Assim, parênteses desempenha seu papel principal em expressões aritméticas, e não apenas em expressões de comparação.
fonte
x + (x = y)
e(x = y) + x
mostraria um comportamento semelhante ao original com operadores de comparação.A questão aqui é a ordem de precedência dos operadores aritmáticos / operadores relacionais dos dois operadores
=
versus==
o dominante é==
(Operators Relational domina), pois precede os=
operadores de atribuição. Apesar da precedência, a ordem de avaliação é LTR (esquerda para a direita). A ordem de avaliação é mostrada após ordem de avaliação. Portanto, independentemente de qualquer avaliação de restrições, é LTR.fonte
É fácil, na segunda comparação à esquerda, a atribuição após atribuir y a x (à esquerda) e comparar 3 == 3. No primeiro exemplo, você está comparando x = 1 com a nova atribuição x = 3. Parece que sempre são feitas declarações de leitura de estado atual da esquerda para a direita de x.
fonte
O tipo de pergunta que você fez é uma pergunta muito boa se você deseja escrever um compilador Java ou programas de teste para verificar se um compilador Java está funcionando corretamente. Em Java, essas duas expressões devem produzir os resultados que você viu. No C ++, por exemplo, eles não precisam - portanto, se alguém reutilizou partes de um compilador C ++ em seu compilador Java, teoricamente você pode achar que o compilador não se comporta como deveria.
Como desenvolvedor de software, escrevendo um código legível, compreensível e sustentável, ambas as versões do seu código seriam consideradas terríveis. Para entender o que o código faz, é preciso saber exatamente como a linguagem Java é definida. Alguém que escreve código Java e C ++ estremeceria ao olhar para o código. Se você precisar perguntar por que uma única linha de código faz o que faz, evite esse código. (Suponho e espero que os caras que responderam corretamente à sua pergunta "por que" também evitem esse código).
fonte