x = x++; は一見「xを1増やして代入している」ように見えますが、JVMレベルのバイトコードの動きを追うと「結局値が変わらない」という挙動になります。内部的にどう処理されているかを詳しく見ていきましょう。
1. ソースコード
public class Sample {
public static void main(String[] args) {
int x = 0;
x = x++;
System.out.println(x);
}
}
Java2. コンパイル後のバイトコード(javap -c Sample の一部)
0: iconst_0 // 定数0をスタックに積む
1: istore_1 // x = 0 をローカル変数1に格納
2: iload_1 // xの値をスタックに積む(ここで0)
3: iinc 1, 1 // ローカル変数1 (x) をインクリメント(x=1になる)
6: istore_1 // スタックトップの値(0)をxに代入 → xが再び0に上書きされる
7: getstatic ...
10: iload_1
11: invokevirtual ...
3. スタックとローカル変数の動き
- ステップ 2 (
iload_1)
ローカル変数xの値(0)をスタックに積む。
→ スタック: [0], ローカル変数: x=0 - ステップ 3 (
iinc 1, 1)
ローカル変数xを直接インクリメント。
→ スタック: [0], ローカル変数: x=1 - ステップ 6 (
istore_1)
スタックトップの値(0)をローカル変数xに格納。
→ スタック: [], ローカル変数: x=0
結果として、インクリメントされた値は捨てられ、元の値が代入されるため、x は変化しません。
4. なぜこうなるのか?
x++は「元の値を返しつつ、変数を1増やす」という式。x = x++;の場合、右辺の「元の値」が左辺に代入されるため、インクリメントの効果が消える。- JVMバイトコード的には「スタックに古い値を積む → ローカル変数を+1 → スタックの古い値を再び代入」という順序になる。
5. 実用的なまとめ
x = x++;は 無意味なコード(結果的にxは変わらない)。- これは「副作用を持つ式を代入に使う」典型的な落とし穴。
- 実際のコードでは避けるべきで、
x++;かx = x + 1;を明示的に書く方が安全。
6. 発展的な話
iinc命令は「ローカル変数を直接インクリメントする」特殊命令で、スタックを使わない。- そのため「スタックに積んだ古い値」と「ローカル変数の更新」がズレてしまい、最後に古い値で上書きされる。
- これは Java言語仕様 (JLS) に沿った正しい動作であり、コンパイラが意図的にこういうバイトコードを生成している。
