Uwaga : pierwotnie opublikowałem kod C # w tej odpowiedzi w celach ilustracyjnych, ponieważ C # umożliwia przekazywanie int
parametrów przez odniesienie do ref
słowa kluczowego. Postanowiłem zaktualizować go o rzeczywisty legalny kod Java, używając pierwszej MutableInt
klasy, którą znalazłem w Google, aby w przybliżeniu to, co ref
robi w C #. Naprawdę nie wiem, czy to pomaga, czy boli odpowiedź. Powiem, że osobiście nie zrobiłem tyle rozwoju Java; więc o ile wiem, można zilustrować ten idiomatyczny sposób.
Być może, jeśli napiszemy metodę równoważną temu, co x++
to uczyni, będzie to jaśniejsze.
public MutableInt postIncrement(MutableInt x) {
int valueBeforeIncrement = x.intValue();
x.add(1);
return new MutableInt(valueBeforeIncrement);
}
Dobrze? Zwiększ przekazaną wartość i zwróć pierwotną wartość: taka jest definicja operatora poinkrementacji.
Zobaczmy teraz, jak wygląda to zachowanie w przykładowym kodzie:
MutableInt x = new MutableInt();
x = postIncrement(x);
postIncrement(x)
robi co? Przyrosty x
, tak. A potem zwraca to, co x
było przed przyrostem . Ta zwracana wartość jest następnie przypisywana do x
.
Tak więc kolejność przypisanych wartości x
to 0, następnie 1, a następnie 0.
To może być jeszcze jaśniejsze, jeśli przepiszemy powyższe:
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.
Twoje przypuszczenie, że po zastąpieniu x
po lewej stronie powyższego zadania słowem y
„widać, że najpierw zwiększa x, a później przypisuje je y”, wydaje mi się mylące. Nie x
przypisuje się tego y
; jest to wartość poprzednio przypisanax
. Naprawdę, wstrzykiwanie y
nie różni się niczym od powyższego scenariusza; po prostu mamy:
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.
Jest więc jasne: x = x++
skutecznie nie zmienia wartości x. Zawsze powoduje, że x ma wartości x 0 , następnie x 0 + 1, a następnie x 0 ponownie.
Aktualizacja : Nawiasem mówiąc, aby nie wątpić, że x
kiedykolwiek zostanie przypisane do 1 „pomiędzy” operacją przyrostową a przypisaniem w powyższym przykładzie, zrzuciłem szybkie demo, aby zilustrować, że ta wartość pośrednia rzeczywiście „istnieje”, chociaż będzie nigdy nie będzie „widoczny” w wątku wykonującym.
Demo wywołuje x = x++;
pętlę, podczas gdy oddzielny wątek ciągle drukuje wartość x
na konsoli.
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);
}
}
}
Poniżej znajduje się fragment wyników powyższego programu. Zwróć uwagę na nieregularne występowanie zarówno 1, jak i 0.
Rozpoczynam wątek w tle ...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1