型変換(自動)の全体像
Java の「自動型変換」は、コンパイラが安全だと判断できる範囲で型を広げたり(拡大変換)、式の計算前に統一の型へ昇格させたりする仕組みです。代表は「拡大プリミティブ変換(widening)」「数値昇格(numeric promotion)」「文字列連結時の自動文字列化」「オートボクシング/アンボクシング」です。逆に「縮小変換(narrowing)」は自動では行われず、明示的なキャストが必要になります。
拡大プリミティブ変換(widening)
安全に広がる方向
int i = 100;
long l = i; // OK: int → long(情報は失われない)
float f = l; // OK: long → float(厳密値ではなくなる可能性はあるが許容される)
double d = f; // OK: float → double
char c = 65;
int x = c; // OK: char → int('A' のコードポイント 65)
Javaプリミティブでは小さい器から大きい器へは自動変換されます(byte → short → int → long → float → double、char → int など)。boolean は他型へも他型からも変換できません。
縮小は自動不可(キャストが必要)
double d = 3.9;
// int n = d; // NG(コンパイルエラー)
int n = (int) d; // OK(明示キャスト、3へ切り捨て)
Java精度や範囲を失う可能性がある縮小は必ずキャストが必要です。キャスト後の値の意味(丸め・オーバーフロー)を理解した上で行いましょう。
数値昇格(演算前の型統一)
整数演算は最低でも int
byte a = 10;
byte b = 20;
// byte s = a + b; // NG(式は int に昇格)
int s = a + b; // OK
Javabyte/short/char の演算はまず int に昇格します。代入先が小さい型なら、必要に応じてキャストします。
byte s2 = (byte) (a + b); // 値が 127 を超えるとオーバーフローに注意
Java混在型は「より広い型」へ
int i = 3;
long l = 4L;
double d = 0.5;
System.out.println(i + l); // 結果は long
System.out.println(l + d); // 結果は double
System.out.println(1 / 2); // 0(int / int → 整数除算)
System.out.println(1 / 2.0); // 0.5(double が含まれるため浮動小数演算)
Java型が混在する式では、より広い型に揃えてから計算されます。整数同士の除算は切り捨てになることを忘れず、必要なら最初から double を混ぜます。
リテラルの型と接尾辞(基礎の落とし穴)
既定の型と接尾辞
long big = 3_000_000_000L; // 整数リテラルは既定で int、long にするなら L
float f = 3.14f; // 小数リテラルは既定で double、float にするなら f
double d = 3.14; // 既定の小数は double
Java整数は既定で int、浮動小数は既定で double。代入先に合わせて L や f を明示すると意図が伝わります。
char と文字コード
char c = 'A'; // 文字リテラル
int code = c; // 65(Unicode コードポイント)
char jp = '\u3042'; // あ(Unicode エスケープ)
Javachar は 16bit のコード単位(UTF-16)です。BMP 外の文字はサロゲートペアになります(コードポイント操作が必要な場面に注意)。
文字列連結の自動変換
String + 任意型で自動文字列化
String s = "n=" + 42; // "n=42"
System.out.println(1 + 2 + "A"); // "3A"(左から順に評価)
System.out.println("A" + 1 + 2); // "A12"
System.out.println("A" + (1 + 2)); // "A3"(意図が明確)
Java+ に String が含まれると、他のオペランドは自動で文字列化されます。評価順の影響が大きいので、曖昧なら括弧で明示します。
オートボクシング/アンボクシング(自動だけど別の仕組み)
プリミティブ ↔ ラッパーの自動変換
Integer ix = 10; // ボクシング(int → Integer)
int n = ix; // アンボクシング(Integer → int)
java.util.List<Integer> list = java.util.List.of(1, 2, 3); // コレクションは参照型
Javaコレクションやジェネリクスは参照型のみなので、プリミティブは自動でラッパー型に包まれ/取り出されます。null のアンボクシングは NullPointerException になるので注意。
Integer nx = null;
// int m = nx; // NPE(アンボクシング時)
Javaよくある落とし穴(重要ポイントの深掘り)
精度損失とオーバーフロー
int big = 1_000_000;
int r = big * big; // オーバーフロー(負になる)
long safe = 1_000_000L * 1_000_000L; // long で計算して回避
Java演算前に「広い型へ昇格させる」工夫が必要です。少なくとも片方を long や double にして計算しましょう。
整数除算の落とし穴
int a = 1, b = 2;
double x = a / b; // 0.0(整数除算の結果 0 が double へ)
double y = a / (double) b; // 0.5(片方を double に)
Java期待が小数なら、演算前に片方を浮動小数へ変換しておくのが安全です。
比較の罠(ラッパーと参照比較)
Integer a1 = 128, a2 = 128;
System.out.println(a1 == a2); // false(参照が別)
System.out.println(a1.equals(a2)); // true(値比較)
Javaラッパー型は参照。== は参照比較、値比較は equals を使います(-128〜127 はキャッシュにより == でも同値に見える場合があり、逆に混乱の元)。
例題で身につける
例 1: 混在型の安全な計算
public class MixedCalc {
public static void main(String[] args) {
int qty = 3;
long unit = 1_000_000_000L;
long total = qty * unit; // int が long に昇格 → 安全
System.out.println(total);
}
}
Java例 2: 整数除算を小数へ
public class DivSafe {
public static void main(String[] args) {
int a = 1, b = 2;
double r1 = a / (double) b; // 0.5
double r2 = (double) a / b; // 0.5(どちらでも可)
System.out.println(r1 + ", " + r2);
}
}
Java例 3: ループでの昇格とキャスト
public class SumBytes {
public static void main(String[] args) {
byte[] arr = {100, 27, 1};
int sum = 0; // まず int で受ける
for (byte v : arr) sum += v; // 演算は int 昇格
byte limited = (byte) Math.min(sum, 127); // 代入時だけ必要ならキャスト
System.out.println(sum + " / " + limited);
}
}
Java仕上げのアドバイス(重要部分のまとめ)
- 拡大変換は自動、縮小はキャストが必要。boolean は変換不可。
- 演算前の数値昇格ルールを理解する(byte/short/char → int、混在は広い型へ)。
- リテラルの既定型(int/double)と接尾辞(L/f)で意図を明確に。
- 文字列連結は自動文字列化されるが、評価順で結果が変わるため括弧で明示。
- オートボクシング/アンボクシングは便利だが、null と
==の罠に注意。 - 精度損失・オーバーフローを避けるため、計算前に十分な広さの型にしてから演算する。
