Nota : Originalmente, eu publiquei o código C # nesta resposta para fins de ilustração, pois o C # permite que você passe int
parâmetros por referência com a ref
palavra - chave. Decidi atualizá-lo com código Java legal real, usando a primeira MutableInt
classe que encontrei no Google para aproximar o que ref
é feito em c #. Não sei dizer se isso ajuda ou prejudica a resposta. Eu direi que pessoalmente não fiz tanto desenvolvimento em Java; então, pelo que sei, poderia haver muito mais maneiras idiomáticas para ilustrar esse ponto.
Talvez se escrevermos um método para fazer o equivalente ao que x++
isso tornará isso mais claro.
public MutableInt postIncrement(MutableInt x) {
int valueBeforeIncrement = x.intValue();
x.add(1);
return new MutableInt(valueBeforeIncrement);
}
Direita? Incremente o valor passado e retorne o valor original: essa é a definição do operador pós-incremento.
Agora, vamos ver como esse comportamento se desenrola no seu código de exemplo:
MutableInt x = new MutableInt();
x = postIncrement(x);
postIncrement(x)
Faz o que? Incrementos x
, sim. E então retorna o que x
estava antes do incremento . Esse valor de retorno é atribuído a x
.
Portanto, a ordem dos valores atribuídos x
é 0, depois 1 e 0.
Isso pode ser mais claro ainda se reescrevermos o acima:
MutableInt x = new MutableInt(); // x is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
x = temp; // Now x is 0 again.
Sua fixação no fato de que, quando você substitui x
no lado esquerdo da atribuição acima por y
", você pode ver que ela primeiro incrementa xe depois atribui a y" me parece confuso. Não é isso x
que está sendo atribuído y
; é o valor anteriormente atribuídox
. Realmente, injetar y
torna as coisas não diferentes do cenário acima; nós simplesmente temos:
MutableInt x = new MutableInt(); // x is 0.
MutableInt y = new MutableInt(); // y is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
y = temp; // y is still 0.
Portanto, é claro: x = x++
efetivamente não altera o valor de x. Sempre faz com que x tenha os valores x 0 , x 0 + 1 e x 0 novamente.
Atualização : Aliás, para que você não duvide que x
seja atribuído a 1 "entre" a operação de incremento e a atribuição no exemplo acima, juntei uma demonstração rápida para ilustrar que esse valor intermediário realmente "existe", embora nunca seja "visto" no thread em execução.
A demo chama x = x++;
em loop enquanto um thread separado imprime continuamente o valor de x
para o console.
public class Main {
public static volatile int x = 0;
public static void main(String[] args) {
LoopingThread t = new LoopingThread();
System.out.println("Starting background thread...");
t.start();
while (true) {
x = x++;
}
}
}
class LoopingThread extends Thread {
public @Override void run() {
while (true) {
System.out.println(Main.x);
}
}
}
Abaixo está um trecho da saída do programa acima. Observe a ocorrência irregular de 1s e 0s.
Iniciando o encadeamento em segundo plano ...
0 0
0 0
1
1
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
1
0 0
1