型変換の概要(キャスト/ボクシング/アンボクシング)
異なる型どうしでデータを受け渡すとき、Javaは「キャスト(型変換)」で数値型を変えたり、「ボクシング/アンボクシング」でプリミティブ型とラッパー型を自動変換します。暗黙的キャストは安全な拡大変換で自動、明示的キャストは縮小変換で開発者が指示します。
ボクシングはプリミティブからラッパーへ、アンボクシングはラッパーからプリミティブへ自動変換する仕組みで、コレクションなど参照型しか扱えない場面で活躍します。
数値の型変換(暗黙/明示キャスト)
暗黙的キャスト(拡大変換)
- 特徴: 小さい器から大きい器へ安全に拡張。コードにキャスト記号は不要。
- 代表的な流れ: byte → short → int → long → float → double。
int x = 100;
long y = x; // OK: int → long(自動)
float f = 5.0f;
double d = f; // OK: float → double(自動)
Java小さい型から大きい型への変換はJavaが自動で行います。
明示的キャスト(縮小変換)
- 特徴: 大きい器から小さい器へ。データ損失の可能性があるため「(型)」で明示が必要。
- 目的: 範囲が収まることを自分で保証する使い方が基本。
long m = 100L;
// short n = m; // コンパイルエラー
short n = (short) m; // OK: long → short(明示キャスト)
double pi = 3.14;
int ip = (int) pi; // 3(小数切り捨て)
Java縮小変換は明示キャストが必須で、範囲外や小数切り捨てに注意します。
よくある落とし穴
- オーバーフロー: 例えば、intの範囲外をshortへキャストすると値が巻き戻る。
- 小数切り捨て: double→intは小数部が消える。
- 算術昇格: 式中で小さい型が自動的にintに昇格し、結果型が変わることがある(例: byte + byte → int)。
- char→int: 文字コード(Unicode値)に変換される。
参照型のキャスト(継承階層)
継承関係にあるオブジェクトは、上位型(親)へは暗黙的に代入でき、下位型(子)へは明示キャストが必要です。実体がその子型でない場合はClassCastExceptionになります。事前にinstanceofで確認するのが安全です。
class Animal { }
class Dog extends Animal { void bark() {} }
Animal a = new Dog(); // 親型への代入(暗黙的)
Dog d = (Dog) a; // 子型へのキャスト(OK: 実体はDog)
d.bark();
// 安全なパターン
if (a instanceof Dog dog) { // Java 16+ パターンマッチ
dog.bark();
} else {
System.out.println("Dogではありません");
}
Java参照型の下位キャストは、実体がその型であることを前提に明示が必要で、instanceof確認が推奨です。
ボクシング/アンボクシング(プリミティブ⇄ラッパー)
基本ルール
- ラッパー型: Boolean, Character, Byte, Short, Integer, Long, Float, Double。各プリミティブに一対一で対応 Qiita。
- ボクシング: プリミティブ → ラッパーへ自動変換。
- アンボクシング: ラッパー → プリミティブへ自動変換。
// ボクシング(自動)
int i = 10;
Integer I = i; // int → Integer
// アンボクシング(自動)
Integer J = Integer.valueOf(20);
int j = J; // Integer → int
// コレクションでの自動変換
import java.util.List;
import java.util.ArrayList;
List<Integer> list = new ArrayList<>();
list.add(123); // int が Integer に自動ボクシング
int first = list.get(0); // Integer が int に自動アンボクシング
Java8種のプリミティブ型にはそれぞれ対応ラッパーがあり、自動で相互変換されます。
注意点(現場でよく起こる)
- nullのアンボクシング: IntegerがnullのままintへアンボクシングするとNullPointerException。null許容ならラッパーのまま扱う、もしくはデフォルト値で回避。
- パフォーマンス: 大量ループでの頻繁なボクシングはGC負荷増。必要に応じてプリミティブ配列や専用APIを選ぶ。
- 比較の落とし穴: ラッパー同士の==は参照比較。数値比較はequalsまたはアンボクシングして比較。
Integer a = 128, b = 128;
System.out.println(a == b); // false(参照が別)
System.out.println(a.equals(b)); // true(値が等しい)
Integer k = null;
// int kk = k; // NullPointerException
int kk = (k != null) ? k : 0; // 安全にデフォルト値
Javanullのアンボクシングは例外の原因になりやすく、比較はequalsやアンボクシングで値比較に統一します。
文字列と数値の変換(受け渡しの定番)
数値→文字列
- valueOf: もっとも簡単で高速。
int n = 42;
String s = String.valueOf(n); // "42"
Java文字列→数値
- parse系: 入力検証と例外処理をセットで。
String s = "123";
int n = Integer.parseInt(s); // 123
double d = Double.parseDouble("3.14"); // 3.14
// 失敗時のハンドリング
try {
int x = Integer.parseInt("12a");
} catch (NumberFormatException e) {
System.out.println("数字ではありません");
}
Java文字列と数値の相互変換はvalueOf/parse系メソッドが基本です。入力不正時はNumberFormatExceptionを捕捉します。
例題と練習(解説つき)
例題1: 暗黙/明示キャストの違い
- ねらい: 拡大と縮小の挙動を体感する。
int i = 100;
long l = i; // 暗黙: OK
double pi = 3.14;
int ip = (int) pi; // 明示: 3(小数切り捨て)
System.out.println(l); // 100
System.out.println(ip); // 3
Java- ポイント: 拡大は自動/縮小は明示。小数が消えることに注意。
例題2: 参照型キャストの安全確認
- ねらい: instanceofで例外を防ぐ。
Object obj = Math.random() > 0.5 ? "hello" : 123;
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
} else {
System.out.println("Stringではありません: " + obj);
}
Java- ポイント: instanceofで型を見てからキャスト。ClassCastExceptionを未然に防ぐ。
例題3: ボクシングの落とし穴(null問題)
- ねらい: nullアンボクシングの例外を理解。
Integer count = null;
// int c = count; // NullPointerException
int c = (count != null) ? count : 0;
System.out.println(c); // 0
Java- ポイント: nullかもしれない値はラッパーで保持し、使う瞬間にデフォルトで安全化。
例題4: 文字列→数値の堅牢変換
- ねらい: 受け入れバリデーション+例外処理。
String input = "007";
try {
int code = Integer.parseInt(input);
System.out.println(code); // 7
} catch (NumberFormatException e) {
System.out.println("数字のみ許可");
}
Java- ポイント: parse時は例外対策必須。先頭ゼロや空文字の扱いも設計に合わせて決める.
小さなレシピ(テンプレ)
- 安全な縮小キャスト:
long v = 500;
int safe = (v >= Integer.MIN_VALUE && v <= Integer.MAX_VALUE) ? (int) v : 0; // 収まらない場合はデフォルト
Java- 比較の統一(ラッパー・null許容):
static boolean eq(Integer a, Integer b) {
return (a == b) || (a != null && a.equals(b));
}
Java- コレクション受け渡し(プリミティブ→ラッパー):
List<Integer> ids = new ArrayList<>();
for (int id : new int[]{1,2,3}) ids.add(id); // 自動ボクシング
Java次の一歩
- 練習案:
- 算術昇格の確認: byte/short/charの加算結果型をprintlnで確かめる。
- オーバーフロー観察: 32768をshortへキャストしたときの値変化を確認。
- nullセーフAPI: OptionalやObjects.requireNonNullで受け渡し契約を明確化。
