Примітка : Спочатку я розмістив код C # у цій відповіді для ілюстрації, оскільки C # дозволяє передавати int
параметри за посиланням на ref
ключове слово. Я вирішив оновити його фактичним юридичним кодом Java, використовуючи перший MutableInt
клас, який я знайшов у Google, щоб наблизитись до того, що ref
робить у C #. Я не можу реально сказати, чи допомагає це чи шкодить відповіді. Я скажу, що особисто я ще не зробив так багато розробок Java; тому, наскільки я знаю, може бути набагато більше ідіоматичних способів проілюструвати цю точку.
Можливо, якщо ми випишемо метод зробити еквівалент того, що x++
він робить, це зробить це зрозумілішим.
public MutableInt postIncrement(MutableInt x) {
int valueBeforeIncrement = x.intValue();
x.add(1);
return new MutableInt(valueBeforeIncrement);
}
Правильно? Збільшити передане значення і повернути початкове значення: ось визначення оператора післярозвитку.
Тепер давайте подивимося, як ця поведінка відтворюється у вашому прикладі коду:
MutableInt x = new MutableInt();
x = postIncrement(x);
postIncrement(x)
робить що? Зростання x
, так. А потім повертає те, що x
було до приросту . Потім це повертається значення присвоюється x
.
Тож порядок призначень присвоєно x
0, то 1, то 0.
Це може бути зрозуміліше, якщо ми перепишемо вище:
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.
Ваша фіксація на тому, що замінюючи x
ліву частину вищезазначеного завдання на y
", ви бачите, що спочатку він збільшується x, а пізніше приписує це y", вражає мене як заплутана. Це не x
призначено y
; це значення, раніше присвоєнеx
. Дійсно, введення ін'єкцій y
не відрізняється від описаного вище сценарію; ми просто отримали:
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.
Тож зрозуміло: x = x++
фактично не змінюється значення x. Завжди це призводить до того, що x має значення x 0 , потім x 0 + 1, а потім x 0 знову.
Оновлення : Між іншим, щоб у вас не виникло сумнівів, що x
коли-небудь присвоюється 1 "між" операцією збільшення та призначенням у наведеному вище прикладі, я зібрав швидку демонстрацію, щоб проілюструвати, що це проміжне значення дійсно "існує", хоча воно буде ніколи не бути "поміченим" на виконуваній нитці.
Демо-виклики x = x++;
в циклі, тоді як окремий потік постійно друкує значення x
консолі.
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);
}
}
}
Нижче наведено уривок виходу вищезгаданої програми. Помітьте нерегулярне виникнення як 1, так і 0.
Початкова нитка фону ...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1