型変換の全体イメージをつかむ
「型変換」は、ある型の値を、別の型として扱えるようにすることです。
Javaでは、主に次の2つの場面でよく出てきます。
- プリミティブ型どうしの変換(
int→doubleなど)=キャスト - プリミティブ型とラッパークラス(
int↔Integerなど)の変換=ボクシング/アンボクシング
まずは「なぜ変換が必要になるのか」をイメージしておきましょう。
たとえば、int の変数と double の変数を足し算するとき、Javaは「型をそろえる」必要があります。
このとき、裏側で自動的に型変換が行われています。
プリミティブ型の型変換(キャスト)
暗黙の型変換(自動でやってくれる変換)
Javaは、安全だと判断できる場合は、自動で型変換をしてくれます。
これを「暗黙の型変換(implicit cast)」と呼びます。
例えば、int から double への変換は、情報が失われないので自動で行われます。
public class ImplicitCastExample {
public static void main(String[] args) {
int x = 10;
double y = x; // int → double に自動変換
System.out.println("x = " + x); // 10
System.out.println("y = " + y); // 10.0
}
}
Javaint の 10 が double の 10.0 に変わっていますが、意味としては同じ値です。
このように「入れ物が大きくなる方向」の変換は、Javaが勝手にやってくれます。
明示的な型変換(キャスト)
逆に、「情報が失われるかもしれない」変換は、自動ではやってくれません。
このとき必要になるのが「キャスト(cast)」です。
public class ExplicitCastExample {
public static void main(String[] args) {
double d = 10.8;
int i = (int) d; // double → int をキャスト
System.out.println("d = " + d); // 10.8
System.out.println("i = " + i); // 10
}
}
Java(int) と書くことで、「小数部分が切り捨てられてもいいから、int にしてくれ」と明示的に指示しています。
ここが重要ポイントで、「キャストするときは、何が失われるかを自分で理解しておく必要がある」ということです。
キャストで起こる「値の欠落」と「オーバーフロー」
キャストには、主に2種類の危険があります。
1つ目は「小数部分が消える」などの情報欠落です。
さきほどの 10.8 → 10 のように、丸めではなく「切り捨て」になることに注意してください。
2つ目は「オーバーフロー」です。
小さい型に大きすぎる値を入れようとすると、値が壊れます。
public class OverflowExample {
public static void main(String[] args) {
int big = 130;
byte small = (byte) big; // byte の範囲を超えている
System.out.println("big = " + big); // 130
System.out.println("small = " + small); // -126 など、意図しない値
}
}
Javabyte は -128 〜 127 までしか表現できないので、130 を入れるとぐるっと回って別の値になってしまいます。
キャストは「コンパイルエラーを消す魔法」ではなく、「自己責任で変換する宣言」だと覚えておくといいです。
ボクシングとアンボクシングのイメージ
ラッパークラスとは何か
Javaには、プリミティブ型を「オブジェクトとして扱うためのクラス」が用意されています。
これを「ラッパークラス(wrapper class)」と呼びます。
代表的な対応は次の通りです。
int↔Integerdouble↔Doubleboolean↔Booleanchar↔Character
プリミティブ型は「ただの値」ですが、ラッパークラスは「オブジェクト」なので、
コレクション(List や Map)に入れたり、null を扱ったりできます。
ボクシング(boxing)とは
ボクシングは、「プリミティブ型 → ラッパークラス」への変換です。
値を「箱に入れてオブジェクト化する」イメージです。
public class BoxingExample {
public static void main(String[] args) {
int n = 10;
Integer boxed = Integer.valueOf(n); // 明示的なボクシング
System.out.println("n = " + n);
System.out.println("boxed = " + boxed);
}
}
JavaJava 5 以降では、次のように書くだけで自動的にボクシングされます(オートボクシング)。
public class AutoBoxingExample {
public static void main(String[] args) {
int n = 10;
Integer boxed = n; // 自動で Integer に変換される(オートボクシング)
System.out.println("boxed = " + boxed);
}
}
Javaアンボクシング(unboxing)とは
アンボクシングは、「ラッパークラス → プリミティブ型」への変換です。
箱から中身の値を取り出すイメージです。
public class UnboxingExample {
public static void main(String[] args) {
Integer boxed = Integer.valueOf(20);
int n = boxed.intValue(); // 明示的なアンボクシング
System.out.println("boxed = " + boxed);
System.out.println("n = " + n);
}
}
Javaこちらも、Java 5 以降は自動で行われます(オートアンボクシング)。
public class AutoUnboxingExample {
public static void main(String[] args) {
Integer boxed = 30;
int n = boxed; // 自動で int に変換される(オートアンボクシング)
System.out.println("n = " + n);
}
}
Javaボクシング/アンボクシングがよく使われる場面
コレクションとプリミティブ型
List や Map などのコレクションは、プリミティブ型を直接扱えません。
そのため、ラッパークラスが必要になります。
import java.util.ArrayList;
import java.util.List;
public class ListBoxingExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(10); // int → Integer にオートボクシング
numbers.add(20);
numbers.add(30);
int first = numbers.get(0); // Integer → int にオートアンボクシング
System.out.println("first = " + first);
}
}
Javaここでは、add(10) のところで int が Integer に自動変換され、get(0) のところで Integer が int に自動変換されています。
「コレクションにプリミティブを入れたいときは、ラッパークラスを使う」と覚えておくとよいです。
メソッドの引数や戻り値での利用
「値がないことを表現したい」とき、プリミティブ型では null を使えません。
このとき、ラッパークラスが役に立ちます。
public class NullableExample {
public static void main(String[] args) {
Integer result = maybeReturnNumber(true);
System.out.println("result = " + result);
Integer noResult = maybeReturnNumber(false);
System.out.println("noResult = " + noResult);
}
static Integer maybeReturnNumber(boolean flag) {
if (flag) {
return 100; // オートボクシングで Integer に
} else {
return null; // 「値がない」を表現できる
}
}
}
Javaプリミティブ型の int では null を返せないので、
「値があるかもしれないし、ないかもしれない」という状況では Integer を使うことがあります。
ボクシング/アンボクシングで気をつけるべき落とし穴
null のアンボクシングで起こる例外
ラッパークラスの変数が null のときに、アンボクシングが起こると NullPointerException になります。
public class NullUnboxingExample {
public static void main(String[] args) {
Integer value = null;
// ここでオートアンボクシングが起こる
int n = value; // 実行時に NullPointerException
System.out.println("n = " + n);
}
}
Java「ラッパークラスをプリミティブに代入するときは、null じゃないか確認する」という癖をつけると安全です。
if (value != null) {
int n = value;
System.out.println("n = " + n);
} else {
System.out.println("value は null です");
}
Javaパフォーマンス面の注意
ボクシング/アンボクシングは「オブジェクトの生成」や「変換処理」を伴うため、
大量に行うとパフォーマンスに影響することがあります。
例えば、ループの中でひたすらボクシング/アンボクシングを繰り返すようなコードは、
できるだけプリミティブ型だけで完結するように書き直した方がよい場合があります。
初心者のうちは「まず正しく動かす」が最優先ですが、
「ラッパークラスはプリミティブより重い」という感覚だけ頭の片隅に置いておくと、後で効いてきます。
型変換の組み合わせ例と練習
プリミティブのキャストとボクシングを組み合わせる
次のコードで、どこで何が起きているかを追いかけてみましょう。
public class MixedConversionExample {
public static void main(String[] args) {
int i = 10;
double d = 3.5;
// 1. 計算時に int が double に暗黙の型変換
double result = i + d; // 10 → 10.0 に変換されて 10.0 + 3.5 = 13.5
// 2. 結果を int にキャスト(小数切り捨て)
int intResult = (int) result; // 13.5 → 13
// 3. int を Integer にオートボクシング
Integer boxed = intResult;
System.out.println("result = " + result);
System.out.println("intResult = " + intResult);
System.out.println("boxed = " + boxed);
}
}
Javaこの中には、暗黙の型変換、キャスト、オートボクシングがすべて登場しています。
「どのタイミングで、どの型からどの型へ変換されているか」を紙に書き出してみると理解が一気に深まります。
ミニ練習問題
問題1
次のコードの実行結果を予想してみてください。
public class Practice1 {
public static void main(String[] args) {
int a = 7;
double b = 2.0;
double c = a / b;
int d = (int) c;
System.out.println("c = " + c);
System.out.println("d = " + d);
}
}
Java「a / b の時点で何型どうしの計算になっているか」「キャストで何が起きるか」を意識して考えてみてください。
問題2
次のコードは実行時にどうなるでしょうか。理由も説明できると完璧です。
public class Practice2 {
public static void main(String[] args) {
Integer x = null;
Integer y = 5;
int sum = x + y;
System.out.println("sum = " + sum);
}
}
Javaここでは、x + y の計算の前後で、どんなアンボクシングが起きているかがポイントです。
まとめ
- プリミティブ型どうしの変換は「キャスト」で行う
- 安全な方向(情報が増える・入れ物が大きくなる)は暗黙の型変換が行われる
- 情報が失われる可能性がある変換は、明示的なキャストが必要
- ラッパークラスはプリミティブ型をオブジェクトとして扱うためのクラス
- プリミティブ → ラッパークラスがボクシング、ラッパークラス → プリミティブがアンボクシング
- オートボクシング/オートアンボクシングにより、見た目はシンプルに書けるが、裏で変換が起きている
nullのアンボクシングはNullPointerExceptionの代表的な原因になる
