代入演算子の全体像
代入演算子は「右側の式を評価して、その結果を左側の変数に入れる」ための基本ツールです。最も基本は =(代入)で、派生として「加算して代入」の += や「乗算して代入」の *= などの複合代入演算子があります。代入は式でもあり、値を返します(右側の値と同じ)。そのため a = b = 10; のようなチェーンも可能ですが、可読性の観点で乱用は避けましょう。
基本の = と評価順序・チェーン
代入の評価順序と式としての代入
代入は右→左の順で評価されます。まず右側の式を完全に計算し、その結果を左の変数へ格納します。代入式自体はその結果値(右側の値)を返すため、連鎖代入や条件式内での代入が可能です。
int a, b;
a = b = 10; // まず b に 10、次に a に 10。式の値は 10
int x = (a = 5) + 2; // a に 5 を入れてから、式は 7 を返す
Java条件式の中での代入(例: if ((x = f()) > 0) ...)は動作しますが、副作用で読み手を混乱させやすいので、初心者は「代入は代入だけ」に分けるのを推奨します。
複合代入の使い方と挙動
よく使う複合代入(算術)
x += y は x = x + y の短縮形で、-=、*=、/=, %= も同様です。読みやすさと一貫性が上がるため、累積・更新では積極的に使います。
int n = 10;
n += 3; // 13
n -= 2; // 11
n *= 4; // 44
n /= 5; // 8(整数割り算:小数切り捨て)
n %= 3; // 2(余り)
Javaビット演算の複合代入
ビットフラグの更新には &=, |=, ^=, <<=, >>=, >>>= が使えます。権限や状態フラグの管理で活躍します。
int READ = 0b0001, WRITE = 0b0010, EXEC = 0b0100;
int perm = READ;
perm |= WRITE; // WRITE を付与(OR)
perm &= ~READ; // READ を外す(AND と NOT)
perm ^= EXEC; // EXEC をトグル(XOR)
Java文字列の +=(連結)
String は不変ですが、+= は「新しい文字列を作って再代入」します。ループでの多用は非効率なので、繰り返し連結は StringBuilder を使うのが定石です。
String s = "Java";
s += " Basics"; // "Java Basics"(新しいインスタンスに置き換わる)
Java型とキャストの落とし穴(重要ポイントの深掘り)
short/byte の複合代入だけ通る理由
short や byte に対して s = s + 1; はコンパイルエラーになります(計算結果が int になるため)。一方で s += 1; は暗黙キャスト込みで許可されます。この仕様差は初心者がよくつまずくポイントです。
short s = 1;
// s = s + 1; // NG(int を short に暗黙変換できない)
s += 1; // OK(複合代入は暗黙にキャストされる)
Javaこの挙動に頼ると型の境界が曖昧になるため、明示的キャストか、最初から int を使う方が安全です。
オーバーフローは例外にならない
整数の加算・乗算は範囲を超えても例外にならず、値が循環します。複合代入でも同じです。大きく増減する可能性があるなら、型を long に上げる、または境界チェックを入れましょう。
int x = Integer.MAX_VALUE;
x += 1; // -2147483648(循環)
Java参照型への代入とミューテーションの誤解(重要ポイントの深掘り)
「参照を代入する」ことの意味
参照型(配列、List、独自クラスなど)では、代入は「参照のコピー」です。代入後、同じオブジェクトを別名で指すことになり、片方からの変更がもう片方にも見えます。
int[] a = {1, 2};
int[] b = a; // 同じ配列参照を共有
b[0] = 99;
System.out.println(a[0]); // 99(a からも変更が見える)
Java「独立したコピー」が欲しいなら、明示的に複製(clone、コンストラクタコピー、ユーティリティ)を作る必要があります。String は不変なのでこの共有による書き換えは起きません。
final と再代入禁止、スコープ(設計視点の深掘り)
final は「参照の再代入」を止める
final 変数は「一度だけ代入可能」で、以降の再代入を禁止します。参照型の final は「参照の入れ替えが禁止」であり、オブジェクト内部の変更まで禁止するわけではありません(不変にしたいなら不変クラスで設計)。
final List<String> list = new java.util.ArrayList<>();
list.add("A"); // OK(中身の変更)
/* list = new ArrayList<>(); */ // NG(再代入)
Javaスコープ内で最小限に宣言する
代入はスコープごとに考えると安全です。必要な箇所に最短のスコープで宣言・初期化し、不要な再代入を避けると、意図が明確でバグの温床が減ります。
実用例で身につける
例 1: 累積計算(複合代入の基本)
public class Accumulate {
public static void main(String[] args) {
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum += i; // 累積
}
System.out.println(sum); // 15
}
}
Java+= は「読む・書く」が一体化していて、累積ロジックを簡潔に保てます。
例 2: ペナルティと下限ゼロ(安全な更新)
public class Penalty {
public static void main(String[] args) {
int points = 10;
points -= 12; // -2
points = Math.max(points, 0); // 下限ゼロへ正規化
System.out.println(points); // 0
}
}
Java更新後に境界を正規化するパターンは、在庫・スコア・残高などで有効です。
例 3: フラグの付与・除去・トグル(ビット複合代入)
public class Flags {
public static void main(String[] args) {
int READ = 1, WRITE = 2, EXEC = 4;
int perm = 0;
perm |= READ; // 付与
perm |= WRITE; // 付与
perm &= ~READ; // 除去
perm ^= EXEC; // トグル
System.out.println(perm); // 状態を数値で表現
}
}
Javaビット複合代入は「意図が見える」更新で、権限や状態の管理に向いています。
仕上げのアドバイス(重要ポイントのまとめ)
- 代入は「右を評価して左に入れる」。式として値を返すが、読みやすさを最優先して副作用の混在は避ける。
- 複合代入は短く明快。
short/byteの暗黙キャストに頼りすぎず、型を揃えるか明示キャストで安全に。 - 参照型の代入は「同じオブジェクトを共有」。独立性が必要ならコピーを作る。
- オーバーフローは沈黙する。範囲を見積もり、必要なら
longや正規化で守る。 - 変更不要なものは
finalにして再代入を禁止。スコープは最小、更新は意図が伝わる形で。
