この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:guest
更新日時:2026-06-11 07:10:02

タイトル: 代入演算子
SEOタイトル: Java 代入演算子の完全ガイド(複合代入 / 暗黙キャスト / final / 文字列連結)

この記事の要点
  • = は右辺の値を左辺に代入。右辺から評価され、戻り値は左辺の値
  • 複合代入: += -= *= /= %= &= |= ^= <<= >>= >>>=
  • 暗黙キャスト: byte b = 1; b += 1; は OK だが b = b + 1 はコンパイルエラー
  • String+= は内部で StringBuilder.append + 再代入
  • final 変数は初期化後の代入不可。再代入しようとするとコンパイルエラー
  • 代入式は値を返す → a = b = c = 0 のようなチェーン代入が可能(推奨はしない)

基本代入

int x = 10;            // 宣言と初期化
x = 20;                // 再代入
String s = "hello";

// 代入式は値を返す
int a, b, c;
a = b = c = 5;         // 右結合: c=5 → b=5 → a=5
System.out.println(a + " " + b + " " + c);   // 5 5 5

// 代入式を条件に使う(注意して)
int n;
while ((n = readInt()) != -1) {
    process(n);
}

複合代入演算子(算術系)

演算子意味等価
+=加算代入a = a + b (※ 暗黙キャスト差あり)
-=減算代入a = a - b
*=乗算代入a = a * b
/=除算代入a = a / b
%=剰余代入a = a % b

複合代入演算子(ビット系)

演算子意味
&=AND 代入
|=OR 代入
^=XOR 代入
<<=左シフト代入
>>=算術右シフト代入
>>>=論理右シフト代入
int flags = 0;
flags |= 0b0001;     // 1 を ON
flags |= 0b0010;     // 2 を ON
flags &= ~0b0001;    // 1 を OFF
flags ^= 0b0010;     // 2 をトグル

int x = 10;
x <<= 2;             // x = x << 2 → 40
x >>= 1;             // x = x >> 1 → 20

int counter = 0;
counter += 5;        // 5
counter -= 1;        // 4
counter *= 3;        // 12
counter %= 5;        // 2

暗黙キャストの落とし穴(重要)

複合代入は結果が左辺の型に自動キャストされますが、通常の代入はキャスト不要を要求します。

byte b = 10;

// ❌ コンパイルエラー: int を byte に代入できない
// 1 は int リテラル、b + 1 の結果も int
b = b + 1;
//     ^ incompatible types: possible lossy conversion from int to byte

// ✅ 明示キャストすれば OK
b = (byte) (b + 1);

// ✅ 複合代入は OK(暗黙でキャストしてくれる)
b += 1;          // OK!
// 内部的には b = (byte) (b + 1) と等価

// 同じ罠が short / char にも
short s = 100;
s = s + 1;       // ❌ エラー
s += 1;          // ✅ OK

// 浮動小数 → 整数の場合
int i = 10;
i *= 1.5;        // OK! 内部で (int) cast → i = 15
// (i = i * 1.5 はコンパイルエラー)

String の += と最適化

String s = "Hello";
s += ", world";        // → s = s + ", world"

// 単発の += は OK だが、ループ内は要注意
// ❌ 遅い: 毎回新しい String 生成
String result = "";
for (int i = 0; i < 1000; i++) {
    result += i + ",";
}

// ✅ StringBuilder を使う
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i).append(",");
}
String result = sb.toString();

// 単発の文字列連結なら javac が StringBuilder に最適化してくれるが、
// ループ内では毎周 new StringBuilder してしまう

final 変数への代入

final int MAX = 100;     // 定数
// MAX = 200;            // ❌ コンパイルエラー

final List<String> list = new ArrayList<>();
// list = new ArrayList<>();  // ❌ 再代入不可
list.add("hello");           // ✅ 中身の変更は OK(final はオブジェクト参照のみ固定)

// ローカル final 変数(ラムダで参照できる)
final int n = 10;
Runnable r = () -> System.out.println(n);
// n = 20;   // ❌ 変更すると上の行もエラーに

// Effectively final (Java 8+)
// 明示的に final と書かなくても、再代入していなければ final 扱い
int m = 10;
Runnable r2 = () -> System.out.println(m);
// m = 20;   // ❌ ここで再代入するとラムダ側でエラー

参照型の代入はアドレスのコピー

List<Integer> a = new ArrayList<>(List.of(1, 2, 3));
List<Integer> b = a;      // ★ 参照のコピー(中身はコピーされない)

b.add(4);
System.out.println(a);    // [1, 2, 3, 4]  ← a も変わって見える

// 中身もコピーしたい場合
List<Integer> c = new ArrayList<>(a);
c.add(5);
System.out.println(a);    // [1, 2, 3, 4]   ← 変わらない
System.out.println(c);    // [1, 2, 3, 4, 5]

// プリミティブ型は常に値コピー
int x = 10;
int y = x;
y = 20;
System.out.println(x);    // 10 ← 影響なし

代入の評価順序

// Java は左から評価、その後右辺評価、その後代入
int[] a = {1, 2, 3};
int i = 0;
a[i] = i = 2;
// 1. 左辺の a[i] のアドレス → a[0] のアドレス
// 2. 右辺評価: i = 2 (これで i は 2 に)
// 3. 代入: a[0] = 2
System.out.println(java.util.Arrays.toString(a));  // [2, 2, 3]
System.out.println(i);                              // 2

// 評価順依存のコードは可読性が悪い → 避けるべし

他言語の代入演算子との比較

機能JavaPythonJavaScript
基本代入x = 1x = 1x = 1
複合代入+=+=+=
べき乗代入無し**=**=
セイウチ演算子(式中代入)無し(代入自体は式):= (3.8+)無し
論理代入無し無し&&= ||= ??=
分割代入無し(パターンマッチ 21+)a, b = 1, 2[a, b] = [1, 2]

FAQ

Q: ++ii += 1 は同じ?
A: ほぼ同じ。++i は前置インクリメント(式の値も増えた後)、i++ は後置(増える前の値を返す)。i += 1 は前置と同等の評価。

Q: なぜ b += 1 はキャストしてくれるのか
A: JLS (Java 言語仕様) で「複合代入演算子は暗黙キャストを含む」と定義されているからです。便利だが情報損失する可能性があるので注意。

Q: チェーン代入 a = b = 0 は推奨される?
A: 動くが読みにくい・初期化漏れに気付きにくい・コーディング規約で禁止されることも。明示的に 2 行に分けるのが推奨。