タイトル: キャスト
SEOタイトル: Java 型キャスト (cast) 完全ガイド
| この記事の要点 |
|
キャストとは
キャスト (cast) は値や参照を別の型として扱う変換です。Java は静的型付け言語なので、型が合わないと代入できません。キャストで明示的に変換します。
プリミティブ型の暗黙キャスト (拡大変換)
情報を失わない方向の変換は自動で行われます。
// 拡大変換: byte → short → int → long → float → double
byte b = 100;
int i = b; // 暗黙 OK
long l = i; // 暗黙 OK
float f = l; // 暗黙 OK
double d = f; // 暗黙 OK
// 変換チェーン
byte → short → int → long
↘ float → double
char → int (16bit → 32bit)
// char → int は OK
char c = 'A';
int code = c; // 65
// boolean だけは他の型と変換不可(int 0/1 とも)
// boolean → int ★ コンパイルエラー
プリミティブ型の明示キャスト (縮小変換)
情報を失う可能性のある方向は明示的に書く必要があります。
double d = 3.7;
int i = (int) d; // ★ (int) で明示。小数切り捨てで 3
double d2 = -3.7;
int i2 = (int) d2; // -3 ★ 0 方向に切り捨て(Math.floor は -4)
// オーバーフロー
int big = 130;
byte b = (byte) big; // -126 ★ ラップアラウンド (byte 範囲 -128〜127)
// 負の値
int neg = -1;
char c = (char) neg; // 0xFFFF (char は符号なし 16bit)
// 切り捨て vs 四捨五入
double v = 3.7;
int floor = (int) v; // 3
int round = (int) Math.round(v); // 4
int ceil = (int) Math.ceil(v); // 4
整数除算と小数除算
int a = 10, b = 3;
// 整数同士の除算は切り捨て
System.out.println(a / b); // 3
// 片方を double にキャストで小数除算
System.out.println((double) a / b); // 3.3333...
System.out.println(a / (double) b); // 3.3333...
System.out.println((double) (a / b)); // 3.0 ★ 計算後にキャスト → 切り捨て後
// よくあるバグ
double avg = sum / count; // sum, count が int だと小数失う
double avg2 = (double) sum / count; // ✅ こちらが正しい
参照型のキャスト
クラス階層上でアップキャスト(広い方向)は暗黙、ダウンキャスト(狭い方向)は明示。
class Animal { }
class Dog extends Animal { void bark() { } }
class Cat extends Animal { void meow() { } }
// アップキャスト(暗黙)
Animal a1 = new Dog(); // OK: Dog is-a Animal
// ダウンキャスト(明示)
Dog d = (Dog) a1; // OK: 実体が Dog なので
d.bark();
// ❌ 実体と違う型へのキャスト
Animal a2 = new Cat();
Dog d2 = (Dog) a2; // ★ ClassCastException at runtime
// 安全にダウンキャスト: instanceof チェック
if (a2 instanceof Dog) {
Dog d3 = (Dog) a2;
d3.bark();
}
// Java 16+ Pattern Matching(短く書ける)
if (a2 instanceof Dog d4) { // ★ キャスト不要、変数 d4 が直接使える
d4.bark();
}
Autoboxing / Unboxing
プリミティブとラッパークラス間の自動変換(Java 5+)。
// Autoboxing: int → Integer
Integer i = 100; // 内部的に Integer.valueOf(100)
List<Integer> list = new ArrayList<>();
list.add(5); // 自動でボクシング
// Unboxing: Integer → int
int n = i; // 内部的に i.intValue()
int sum = 0;
for (Integer x : list) {
sum += x; // Unboxing
}
// ★ NullPointerException の罠
Integer maybe = null;
int v = maybe; // ★ NullPointerException
// ★ Integer キャッシュ (-128〜127)
Integer a = 127, b = 127;
System.out.println(a == b); // true ★ キャッシュで同一参照
Integer c = 128, d = 128;
System.out.println(c == d); // false ★ 別インスタンス
System.out.println(c.equals(d)); // true
文字列との変換
// String → int
int n = Integer.parseInt("42"); // 42
int n2 = Integer.parseInt("0x1A", 16); // 26
// Integer.parseInt("abc"); → NumberFormatException
// int → String
String s1 = String.valueOf(42); // "42"
String s2 = Integer.toString(42); // "42"
String s3 = "" + 42; // "42" ★ 連結(やや非推奨)
// double → String / String → double
String ds = String.valueOf(3.14);
double dv = Double.parseDouble("3.14");
// Object → String
Object o = new Date();
String os = o.toString(); // toString() を呼ぶ
String os2 = String.valueOf(o); // null セーフ(null なら "null")
キャストでよくあるエラー
| エラー | 原因 | 対処 |
|---|---|---|
ClassCastException | 実際の型と違うクラスへキャスト | instanceof で事前確認 |
NumberFormatException | 数値でない文字列を parseInt | try-catch / 正規表現バリデーション |
NullPointerException | null を Unboxing | Null チェック / Optional |
| 意図しない切捨て | 整数除算で小数になるはずが int に | 事前に (double) キャスト |
| オーバーフロー | 大きな値を小さな型に | 受け側を long / Math.toIntExact |
ジェネリクスとキャスト
// ジェネリクス: コンパイル時の型チェック
List<String> strs = new ArrayList<>();
strs.add("hello");
String s = strs.get(0); // ★ キャスト不要
// raw type は警告
List raw = new ArrayList();
raw.add(42);
String x = (String) raw.get(0); // ★ ClassCastException 実行時
// ワイルドカード
List<? extends Number> nums = new ArrayList<Integer>();
Number n = nums.get(0); // Number として受ける(Integer かもしれない)
// 型消去 (Type Erasure) の影響
public <T> T cast(Object o) {
return (T) o; // unchecked cast 警告
}
Pattern Matching for instanceof (Java 16+)
Object obj = "hello";
// Java 15 まで
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.toUpperCase());
}
// Java 16+
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}
// 複雑な条件と組み合わせ
if (obj instanceof String s && s.length() > 5) {
// ...
}
// switch でも (Java 21+)
String result = switch (obj) {
case Integer i -> "int " + i;
case String s -> "str " + s.length();
case int[] arr -> "arr len " + arr.length;
case null -> "null";
default -> "unknown";
};
FAQ
Q: (int) と Math.round() の違いは?
A: (int) は 0 方向に切り捨て(-3.7 → -3)、Math.round() は四捨五入(-3.7 → -4、3.5 → 4)。
Q: (double) (a / b) と (double) a / b の違いは?
A: 前者は int 除算後に double 変換(切捨てが先)、後者は double 除算(小数で計算)。優先順位の罠です。
Q: 配列のキャストは?
A: Object[] → String[] は実行時に ArrayStoreException の可能性。Arrays.copyOf(arr, len, String[].class) や明示キャストを使う。