タイトル: 代入演算子
SEOタイトル: Java 代入演算子完全ガイド(=, +=, -=, *=, ビット演算複合, 連鎖代入)
| この記事の要点 |
|
基本代入 =
右辺の値 (式の評価結果) を左辺の変数に格納する。代入式自体も値を持ち、左辺に代入された値が式の値となるため a = b = c のような連鎖が書けます。
int a = 10;
double d = 3.14;
String s = "hello";
boolean flag = true;
// 連鎖代入 (右結合)
int x, y, z;
x = y = z = 100; // z=100 → y=100 → x=100
// 代入式は値を持つ
int n;
System.out.println(n = 5); // 5 が出力され、n には 5 が代入される
複合代入演算子一覧
| 演算子 | 等価式 | 用途 |
|---|---|---|
+= | a = a + b | 加算 / 文字列連結 |
-= | a = a - b | 減算 |
*= | a = a * b | 乗算 |
/= | a = a / b | 除算 (整数同士なら切り捨て) |
%= | a = a % b | 剰余 |
&= | a = a & b | ビット AND |
|= | a = a | b | ビット OR |
^= | a = a ^ b | ビット XOR |
<<= | a = a << b | 左シフト |
>>= | a = a >> b | 右シフト (符号維持) |
>>>= | a = a >>> b | 右シフト (符号無視 / 論理シフト) |
int n = 10;
n += 5; // n = 15
n -= 3; // n = 12
n *= 2; // n = 24
n /= 5; // n = 4
n %= 3; // n = 1
int flags = 0b1010;
flags |= 0b0100; // 0b1110 (ビット ON)
flags &= 0b1100; // 0b1100 (マスク)
flags ^= 0b0010; // 0b1110 (反転)
// 文字列連結
String s = "Hello";
s += ", World"; // "Hello, World"
暗黙のキャスト (落とし穴)
複合代入 a += b は a = (型)(a + b) 相当で、左辺の型に自動キャストされます。普通の a = a + b ではコンパイルエラーになるケースも、複合代入なら通ってしまうため注意:
byte b = 10;
// ❌ コンパイルエラー: b + 1 は int になる
// b = b + 1;
// ^^^^^^ incompatible types: possible lossy conversion from int to byte
// ✅ 複合代入は暗黙キャストでコンパイル可
b += 1; // = (byte)(b + 1)
// ⚠️ ただし精度落ちのバグに注意
byte x = 100;
x += 1.5; // = (byte)(100 + 1.5) = (byte)101.5 = 101
// 暗黙キャストで小数切り捨て
参照型の代入は参照コピー
プリミティブ (int / double / boolean 等) は値コピー、参照型 (オブジェクト / 配列) は参照のコピーです。
// プリミティブ: 値コピー
int a = 10;
int b = a;
b = 20;
System.out.println(a); // 10 (a は変わらない)
// 参照型: 参照コピー
int[] arr1 = {1, 2, 3};
int[] arr2 = arr1; // 同じ配列を指す
arr2[0] = 99;
System.out.println(arr1[0]); // 99 (arr1 も変わる!)
// 独立したコピーが欲しいなら明示的に
int[] arr3 = arr1.clone();
arr3[0] = 0;
System.out.println(arr1[0]); // 99 (arr3 は別物)
// String は immutable なので参照コピーでも事故りにくい
String s1 = "hello";
String s2 = s1;
s2 = "world"; // s1 は "hello" のまま (再代入は別オブジェクトを指すだけ)
AutoBoxing と代入
プリミティブとそのラッパー型 (Integer / Double 等) の間は自動変換 (AutoBoxing / UnBoxing) が働きます。
Integer boxed = 10; // AutoBoxing: int → Integer.valueOf(10)
int primitive = boxed; // UnBoxing: boxed.intValue()
// null をプリミティブに UnBox すると NullPointerException
Integer maybe = null;
int n = maybe; // ❌ NullPointerException
// コレクション要素への代入
List list = new ArrayList<>();
list.add(1); // AutoBoxing
list.add(2);
int sum = 0;
for (int v : list) sum += v; // UnBoxing
配列要素 / フィールドへの代入
int[] arr = new int[5];
arr[0] = 100; // 配列要素代入
arr[arr.length - 1] = 999;
User u = new User();
u.name = "Taro"; // フィールド代入 (public の場合)
u.setName("Taro"); // setter 経由 (推奨)
// インデックス計算式も使える
int i = 2;
arr[i + 1] = 50;
他言語との比較
| 演算子 | Java | PHP | JavaScript | Python |
|---|---|---|---|---|
= | OK | OK | OK | OK |
+= 他算術系 | OK | OK | OK | OK |
??= (null 合体代入) | × | OK (7.4+) | OK (ES2021+) | × |
||= / &&= | × | × | OK (ES2021+) | × |
連鎖代入 a = b = c | OK | OK | OK | OK |
評価順序: 右から左
int a = 1, b = 2, c = 3;
// 代入は右結合 → a = (b = (c = 5))
a = b = c = 5;
// 評価順: c = 5 → b = 5 → a = 5
// 結果: a = b = c = 5
// 副作用がある場合は要注意
int[] arr = {0, 0, 0};
int i = 0;
arr[i++] = i++;
// 左辺で arr[0] を確保し i=1、右辺で i=1 を代入し i=2
// arr = {1, 0, 0}, i = 2
FAQ
Q: i++ と ++i はどちらが速い?
A: プリミティブに対しては JIT 最適化されるためほぼ差なし。意味が違うので結果から選ぶこと。
Q: String s += "x" をループで使うと遅い?
A: はい。毎回新しい String が生成されます。ループ内では StringBuilder.append() を使う。
Q: final 変数に複合代入はできる?
A: できません。final は一度代入したら再代入禁止なので、final int n = 0; n += 1; はコンパイルエラー。