Revised: May/4th/2008
オートボクシングとアンボクシングは、J2SE 5.0 (Tiger)で追加された機能です。
プリミティブ型とオブジェクト型を自動変換する仕様をオートボクシングと呼び、逆に、オブジェクト型からプリミティブ型に自動変換する機能をアンボクシングと呼びます。
| プリミティブ型 | ラッパークラス | 概要 |
|---|---|---|
boolean | Boolean | ブーリアン(ブール代数)。trueかfalse。 |
char | Character | 16ビットUnicode UTF-16コードユニット(16ビット符号なし整数) |
byte | Byte | 8ビット符号付整数(2の補数表現) |
short | Short | 16ビット符号付整数(2の補数表現) |
int | Integer | 32ビット符号付整数(2の補数表現) |
long | Long | 64ビット符号付整数(2の補数表現) |
float | Float | 32ビットIEEE 754符号付浮動小数点数 |
double | Double | 64ビットIEEE 754符号付浮動小数点数 |
例えば、Integer型の場合は次の通りです。
// J2SE 1.4以下 Integer intObj = new Integer(127); int i = intObj.intValue(); // J2SE 5.0以上 Integer intObj = 127; // ボクシング int i = intObj; // アンボクシング
この機能は、java.util.Collectionクラスへの値の代入/取得で、Object型のみしか許されない場合などに威力を発揮します。従来は、Collectionクラスへ値を代入するときは、ラッパークラス型へ明示的に変換する必要がありました。逆に、ラッパークラスに対して算術演算子を作用させたい場合は、intValue()などのメソッドでプリミティブ型に変換する必要がありました。J2SE 5.0で導入された本仕様により、それらの変換は自動化されました。
次に例を挙げます:
class TestAutoboxing {
public static void main(String[] args) {
Double d = Double.valueOf(args[0]); // インスタンス化
while (true) {
d *= 1.1; // double型 -> Double型: アンボクシング
if (d > 10000) break; // Double型 -> double型: アンボクシング
}
System.out.println(d);
}
}
D:\java>javac TestAutoboxing.java D:\java>java TestAutoboxing 1024 10086.126260027011 D:\java>
この機能によって、算術演算子が作用する式中では、プリミティブ型とラッパークラス型が区別されなくなりました。非常に便利なのですが、equals()と==については、他の参照型変数と同様の注意が必要です。ラッパークラス型はオブジェクトなので、メモリ上で別のインスタンスであればfalseが返ります。
プリミティブ型の拡張型変換はサポートされますが、ラッパー型ではサポートされません。
次の例は、Integer型オブジェクトをdouble型に代入しています。これは正しいコードです。
class TestBoxingCast {
public static void main(String[] args) {
Integer i = 1024;
double d = i; // Integer -gt; int -> double
System.out.println(d);
}
}
C:\java>javac TestBoxingCast.java C:\java>java TestBoxingCast 1024.0 C:\java>
一方、次のコードは正しくありません。Integer型をDouble型に代入しようとしていますが、コンパイル時に「互換性のない型」が報告されてエラーとなります。
class TestBoxingCast2 {
public static void main(String[] args) {
Integer i = 1024;
Double d = i; // Integer -> Double
System.out.println(d);
}
}
C:\java>javac TestBoxingCast2.java
TestBoxingCast2.java:4: 互換性のない型
検出値 : java.lang.Integer
期待値 : java.lang.Double
Double d = i; // Integer -> Double
^
エラー 1 個
C:\java>
ラッパークラス間の拡張型変換はサポートされません。スーパークラスNumber型へ代入して、doubleValue()などのメソッドを利用することが考えられます。
ラッパー型はオブジェクトなので、equals()と==は別の意味を持ちます。前者は等価であることが比較され、後者はメモリ上の実体が同じであるかが比較されます。算術演算子の作用によってボクシング変換が実施されますが、演算子==に対してはボクシング変換は自動化されていません。
valueOf()のすすめJ2SE 5.0では、各ラッパークラスのメソッドvalueOf()が拡張されています。API仕様では、このメソッドが頻繁に要求される値をキャッシュするので、操作に必要な領域や時間がはるかに少なくて済む場合が多い
とされています。つまり、valueOf()が実行されると、既に存在するオブジェクトが検索され、存在する場合はそのオブジェクトへの参照が返されることがあるということです。
class TestValueOf {
public static void main(String[] args) {
Integer i1 = 1024;
Integer i2 = 1024;
Double d1 = Double.valueOf(6.626 0693E-34);
Double d2 = Double.valueOf(6.626 0693E-34);
System.out.println("i1 == i2: " + (i1 == i2) + "\t: " + i1);
System.out.println("d1 == d2: " + (d1 == d2) + "\t: " + d1);
}
}
残念ながら、上記の実行結果はtureを返しませんでした。しかし、お作法として、コンストラクタよりもvalueOf()を優先的に使うべきでしょう。ラッパークラスのインスタンスは、String型と同様immutableであるので、同じ値を表すオブジェクトを複数生成しても、リソースの無駄です。
但し、ある値については、String型オブジェクトのように、別の場所で生成されたオブジェクトでも、==がtrueを返します。
| 符号付整数 | -128~127 |
| 文字 | \u0000~\u007f |
| ブーリアン | true, false |
つまり、byteの範囲内のリテラルは、オブジェクトがキャッシュされて再利用されるようです。
class TestImmutable {
public static void main(String[] args) {
Integer i1_1 = -128; // internedな下限
Integer i1_2 = -128;
Integer i2_1 = 127; // internedな上限
Integer i2_2 = 127;
Integer i3_1 = i2_1 + 1; // internedでない
Integer i3_2 = i2_1 + 1;
Integer i4_1 = i1_1 -1;
Integer i4_2 = i1_1 - 1;
Character c1_1 = '\u0000'; // internedな下限
Character c1_2 = '\u0000';
Character c2_1 = '\u007f'; // internedな上限
Character c2_2 = '\u007f';
Character c3_1 = '\u0080'; // internedでない
Character c3_2 = '\u0080';
Boolean b1_1 = true; // ブール代数はinterned
Boolean b1_2 = true;
Boolean b2_1 = false;
Boolean b2_2 = false;
// 比較文字列: 評価結果: 評価対象の値
System.out.println("i1_1 == i1_2: " + (i1_1 == i1_2) + ": " + i1_1);
System.out.println("i2_1 == i2_2: " + (i2_1 == i2_2) + ": " + i2_1);
System.out.println("i3_1 == i3_2: " + (i3_1 == i3_2) + ": " + i3_1);
System.out.println("i4_1 == i4_2: " + (i4_1 == i4_2) + ": " + i4_1);
System.out.println("c1_1 == c1_2: " + (c1_1 == c1_2) + ": " + c1_1);
System.out.println("c2_1 == c2_2: " + (c2_1 == c2_2) + ": " + c2_1);
System.out.println("c3_1 == c3_2: " + (c3_1 == c3_2) + ": " + c3_1);
System.out.println("b1_1 == b1_2: " + (b1_1 == b1_2) + ": " + b1_1);
System.out.println("b2_1 == b2_2: " + (b2_1 == b2_2) + ": " + b2_1);
}
}
D:\java>javac TestImmutable.java D:\java>java TestImmutable i1_1 == i1_2: true: -128 i2_1 == i2_2: true: 127 i3_1 == i3_2: false: 128 i4_1 == i4_2: false: -129 c1_1 == c1_2: true: D:\java>
JVMの実装に依存した非常にトリッキーな性質なので、この性質に頼ったコーディングはすべきではありません。コード範囲中でも、値によって評価結果が変わる旨を認識して、比較はequals()で行うようにお勧めします。