ラッパークラスとは、Javaにおいてプリミティブなデータ型を包む、または囲むクラスです。これらのクラスは、Java言語においてプリミティブなデータ型にオブジェクト指向のインターフェースを提供するために使用されます。
Javaには、8つのラッパークラスがあります。
これらのクラスはそれぞれ対応するプリミティブ型と、プリミティブ値に対する操作を実行するために使用できる追加のメソッドを持っています。ラッパークラスは、一度設定すると値を変更することができない不変オブジェクトとして設定されています。
Javaでラッパークラスオブジェクトを作成するには、valueOf()メソッドを使います。valueOf()メソッドは静的メソッドで、受け取った値を持つラッパークラスのインスタンスを返します。
int value = 5;
Integer wrapper = Integer.valueOf(value);
コンストラクタを使ってインスタンスを作成することもできますが、Java9以降は非推奨となっています。その理由は、ラッパークラスは一度作成されるとその値を変更することができず、同じ値を何度も使用する場合にオブジェクトを複数作成することになってしまうからです。これらのオブジェクトが他の目的で必要でない場合、潜在的にメモリの無駄遣いになる可能性があります。
int value = 5;
Integer wrapper = new Integer(value); // 非推奨
valueOfメソッドは、頻繁に使用される値をキャッシュするためメモリや計算量を節約できます。新しくインスタンスを作成する場合は、コンストラクタを使用せず、valueOfメソッドを使うほうがよいでしょう。
Javaにおけるラッパーオブジェクトに関して、等号演算子(==)とequalsメソッドの動作は少し混乱しやすいので、注意が必要です。
演算子を利用してラッパーオブジェクトを比較する場合、参照一致をチェックします。つまり、両方のオブジェクトがメモリー上で同じオブジェクトかどうかをチェックを行います。
参照一致をチェックする場合、通常はequals()メソッドを使わなければ等価判定ができません。しかし、ラッパークラスの特定の値に関しては==演算子で判定することができます。JDK11でのIntegerのvalueOfメソッドの実装は以下のようになっています。
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// プロパティで高い値を設定
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// 配列の最大サイズはInteger.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// プロパティが int にパースできない場合は無視
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// 範囲 [-128, 127] (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerクラスのvalueOfメソッドは、-128〜127の範囲ではIntegerCacheクラスにキャッシュされた値を返し、それ以外は新しいIntegerインスタンスを生成して返しています。つまり、2つのラッパーオブジェクトが-128〜127の範囲で同じ値を持つ場合、その2つは同じインスタンスを指しているため、等号演算子(==)で比較するとtrueが返されます。ラッパークラスごとのキャッシュ範囲は以下のようになります。
この範囲外の値の場合、valueOfメソッドは呼び出されるたびに新しいラッパーオブジェクトを生成するため、これらのオブジェクトを比較する際に==演算子はfalseを返します。equalsメソッドは、JavaのすべてのクラスのスーパークラスであるObjectクラスで定義されているメソッドです。これは、2つのオブジェクトの等価性を比較するために使用されます。
Javaにおけるラッパークラスの場合、equalsメソッドはその参照ではなく、ラッパーのオブジェクトの値を比較するようにオーバーライドされます。これは、もし2つのラッパーオブジェクトが同じ値を持つ場合、たとえメモリ上では同じオブジェクトでなくても、equalsメソッドはtrueを返すことを意味します。またequalsメソッドはオブジェクトの比較にしか使えず、プリミティブ値の比較には使えません。
class Main{
public static void main(String[] args){
Integer a = 128;
Integer b = 128;
Integer c = 127;
Integer d = 127;
int e = 127;
int f = 127;
System.out.println(a==b); // false
System.out.println(a.equals(b)); // true
System.out.println(c==d); // true
System.out.println(c.equals(d)); // true
System.out.println(e==f); // true
// System.out.println(e.equals(f)); // プリミティブには使えません
}
}
Javaでラッパーオブジェクトをプリミティブ型に変換するには、ラッパークラスのtypeValue()メソッドを利用します。このメソッドは、ラッパーオブジェクトが表現するプリミティブ値を返します。
例えば、Integerオブジェクトをint値に変換するには、intValue()メソッドを使用します。wrapperは値100を持つIntegerオブジェクト、valueは同じ値を持つint型の変数です。
Integer wrapper = 100;
int value = wrapper.intValue();
System.out.println(value); // 100
同じように他のラッパークラスも見てみましょう。
Double wrapper = 3.14;
double value = wrapper.doubleValue();
System.out.println(value); // 3.14
Character wrapper = 'a';
char value = wrapper.charValue();
System.out.println(value);
valueOf()メソッドを使用してプリミティブ値からラッパークラスへ変換、typeValue()メソッドを使用してラッパークラスからからプリミティブ値への変換ができるようになりました。実は、これらのメソッドを使わずに、簡単に相互変換を自動で行なってくれる仕組みがあります。
プリミティブ値からラッパークラスへの自動変換をオートボクシング、逆に、ラッパークラスからプリミティブ値への自動変換は、オートアンボクシングと呼ばれています。
int value = 100;
Integer wrapper = value; // オートボクシング
この場合、変数valueは値100のint型で、変数wrapperは同じ値を表すIntegerオブジェクトです。プリミティブ値からラッパーオブジェクトへの変換は、Javaコンパイラが自動的に行います。
Integer wrapper = 100;
int value = wrapper; // オートアンボクシング
この場合、変数wrapperは値100を持つIntegerオブジェクトで、変数valueは同じ値を持つint型です。ラッパーオブジェクトからプリミティブ値への変換は、Javaコンパイラによって自動的に行われます。
public class Main {
public static void main(String[] args) {
// int値 -> Integerオブジェクトに変換:ボクシング
int a = 100;
Integer i1 = Integer.valueOf(a);
// オートボクシングは、構文の単なる省略記法で、コンパイラは内部でInteger.valueOf(a)を呼び出すようになります
Integer i2 = a;
System.out.println(a + i1 + i2);
}
}
Javaにおけるラッパークラスは、文字列を扱うために使用できるいくつかのメソッドを提供しています。ラッパークラスのvalueOfメソッドを使うと、プリミティブ値の文字列表現をパースして,同じ値を表すラッパークラスオブジェクトを返すことができます。
String stringValue = "100";
Integer wrapper = Integer.valueOf(stringValue); // 100
この場合、IntegerクラスのvalueOfメソッドは、文字列値"100"を解析し,同じ値を表すIntegerオブジェクトを返します。ラッパークラスのtoStringメソッドは,ラッパークラスのオブジェクトをその値の文字列表現に変換するために利用できます。
Integer wrapper = 100;
String stringValue = wrapper.toString(); // "100"
この場合、IntegerクラスのtoStringメソッドは、Integerオブジェクトをその値の文字列表現に変換します。
また、IntegerクラスのparseIntメソッドを使うと、整数の文字列表現をパースしてintプリミティブを返すことができます。
String stringValue = "100";
int intValue = Integer.parseInt(stringValue);
この場合、IntegerクラスのparseIntメソッドは、文字列値"100"を解析し、同じ値を表すintプリミティブを返します。これらのメソッドは、Javaで文字列を扱う際に、プリミティブ値とその文字列表現との変換を簡単に行うことができるので、非常に便利になります。
valueOfとparseIntメソッドは、文字列の値が目的のプリミティブ型として解析できない場合、NumberFormatExceptionをスローすることに注意が必要です。
Javaのラッパークラスは、NULL値を格納することができます。
Integer wrapper = null;
この場合、変数wrapperは特定のプリミティブ値を表すラッパーオブジェクトではなく、NULLのIntegerオブジェクトです。
NULL値はプリミティブ値と同じではないことに注意が必要で、NULLのラッパーオブジェクトをプリミティブ値のように使おうとするとNullPointerExceptionが発生します。
Integer wrapper = null;
int value = wrapper; // NullPointerExceptionをスローします
ラッパークラスからプリミティブへの変換が必要な場合、NullPointerExceptionが発生しないようにNULL除外の条件を付けると安全に変換できます。
Integer wrapper = null;
if (wrapper != null) {
int value = wrapper.intValue(); // ラッパーオブジェクトを安全にアンボクシングする
}
Javaではプリミティブ型をオブジェクトに変換することが必要な場合があります。例えば、ArrayList, HashMap, LinkedListなどのコレクションクラスは、プリミティブ型は格納せず参照型のみを格納するため、この場合ラッパークラスが役に立ちます。
// List<int> list = new ArrayList<int>(); // 無効
List<Integer> list = new ArrayList<Integer>(); // 有効
// 自動的にIntegerクラスに変換されて格納されます
list.add(10);
// 自動的にint型の値に変換されて取り出されます
int num = list.get(0);