ボックス化

ボックス化とは、プログラミング言語において値型をオブジェクト型に変換すること。逆に、ボックス化されたオブジェクトを値型に戻すことをボックス化解除と呼ぶ。

1. 概要
-----

Javaや.NET Frameworkなどの環境においては、値型と参照型という根本的に異なる二種類の型が存在する。参照型のインスタンスはヒープ上の独立した領域に確保される。値型は文脈によって確保される場所は異なるものの、いずれにせよメモリ上に連続的に確保される。

このように値型と参照型とは根本的に性質の異なるものであるが、さまざまな理由により、値型を参照型に型変換して運用することが必要となる場合がある。Javaにおいて整数型や浮動小数点数型といったプリミティブ型はオブジェクトではなく、配列以外のコレクションにプリミティブ型を直接格納することができないため、一度参照型でラッピングしてから格納する必要がある。このラッピング処理をボックス化と呼ぶ。各プリミティブ型をラップするための専用クラスは標準Javaクラスライブラリに用意されているため、プログラマが自ら定義する必要はない。

次はJavaによる例である。プリミティブ型のひとつであるint型の値を、対応するプリミティブラッパークラスであるname=java.lang.Integer型のオブジェクトにボックス化し、さらにそれをint型にボックス化解除している。ボックス化により、値はラッパークラス内のカプセル化されたフィールドにて保持されるが、ラッパークラスの使用者は詳細を知る必要はない。 int iv1 = 100; Integer iw = new Integer; // ボックス化。 int iv2 = iw.intValue; // ボックス化解除。

一見単純な型変換のように見えるが、ボックス化においてはヒープ上に新しく領域を確保し、そこに値型のデータをコピーするという操作が行われているため注意が必要である。頻繁なボックス化の発生は性能の低下を引き起こす。一方、ボックス化解除ではデータのコピーが行われるだけであり、ヒープに領域を確保する必要がないのでパフォーマンスコストは比較的小さい。

なお、ゼロ近傍の整数などのよく使われる数値に対して使いまわせるように事前生成されている、ラッパーオブジェクトのキャッシュを優先的に利用できるようにするため、通例name=Integer.Integerコンストラクタよりもname=Integer.valueOfメソッドなどの使用が好ましい。

int iv1 = 100; Integer iw = Integer.valueOf; // ボックス化。

2. 関数型言語におけるボックス化
-----------------

関数型言語でも用いられることがあるが、オブジェクト指向のそれとの関連性はまったくない。ボックス型は単なる間接参照であり、Javaのプリミティブ型に相当する型は非ボックス型やプリミティブ型と呼ばれ、直接参照される。関数型言語のひとつであるHaskellでは、そのデファクトスタンダードな処理系であるGHCに対してコンパイラオプションを指定し、なおかつ非ボックス型であることを随所で明示しない限り、非ボックス型を用いることができない。にもかかわらず、知らず識らずのうちに利用している、馴染み深い型でもある。IO型など、副作用を表現する型がその典型例である。

3. 自動ボックス化
----------

前述のボックス化・ボックス化解除の操作を暗黙的に行なうことを、自動ボックス化および自動ボックス化解除と呼ぶ。

自動ボックス化はJava SE 5  で追加され、JSR 201で宣言されている。自動ボックス化がサポートされた処理系では、対となる逆の操作、自動ボックス化解除もサポートされる。

以下のコードは自動ボックス化・自動ボックス化解除の例である。 int iv1 = 100; Integer iw = iv1; // ボックス化。 int iv2 = iw; // ボックス化解除。 Javaコンパイラによって、プリミティブ型とプリミティブラッパークラス間の暗黙変換は、前述のようなボックス化・ボックス化解除を実行するコードに展開される。

3.1. .NET
---------

.NETにおいては、概念上、値型も含めすべての型はSystem.Object型の派生型であるため、この型への暗黙的なアップキャストを行なうことができる。すべての値型はSystem.ValueTypeクラスから派生し、その基底クラスであるSystem.Objectへの暗黙的なボックス化が可能となっている。一方、ボックス化解除はダウンキャストにより明示的に実行する必要がある。

C#の例を示す。 int iv1 = 100; object iw = iv1; // 暗黙的なボックス化。 int iv2 = iw; // ボックス化解除。

明示的なボックス化も可能ではあるが、通例使われない。 object iw = iv1; // 明示的なボックス化。

4. コレクションの例
-----------

Javaでは、配列以外のコレクションには参照型のみ追加することができる。プリミティブ型のデータをそのまま要素として追加することはできない。 そこで、プリミティブ型のデータは対応するプリミティブラッパークラスのオブジェクトにボックス化して追加し、取り出すときにはダウンキャストしてからボックス化解除する。 import java.util.*; ... List list = new ArrayList; // Object 型のみ格納できる。 //list.add; // Java 1.4 以前ではコンパイルエラー。 list.add); // Integer 型にボックス化してリストに追加する。 Integer iw = list.get; // Object 型から Integer 型にダウンキャスト。 int iv = iw.intValue; // Integer 型からボックス化解除。

Java 1.5以降ではジェネリクスおよび自動ボックス化をサポートするので、プリミティブ型のデータもあたかもオブジェクトのように扱うことができる。ただしコレクションには参照型のみ追加できること、またボックス化・ボックス化解除が実行されることには変わりない。Javaのジェネリクスは型安全であることが保証されているだけであり、ジェネリクスを使わない場合と比べてパフォーマンス上の優位性はない。

List list = new ArrayList; // Integer 型のみ格納できる。 list.add; // int 型の値から自動的に Integer 型のオブジェクトにボックス化される。 int iv = list.get; // Integer 型のオブジェクトから自動的に int 型の値にボックス化解除される。

4.1. .NET
---------

.NET 1.1まではジェネリクスをサポートしなかったため、配列以外のコレクションにはJava同様にボックス化・ボックス化解除を利用する必要があった。 using System.Collections; ... IList list = new ArrayList; // object 型のみ格納できる。 list.Add; // object 型にボックス化してリストに追加する。 object iw = list[0]; int iv = iw; // object 型から int 型にダウンキャストしてボックス化解除。

一方、.NET 2.0  以降のジェネリクスでは値型を直接扱うことができ、Javaと違ってボックス化・ボックス化解除は発生しない。 using System.Collections.Generic; ... IList list = new List; // int 型のみ格納できる。 list.Add; int iv = list[0];

5. 脚注
-----

6. 関連項目
-------

型変換
Java Community Process - Java技術の標準化プロセス

7. 外部リンク
--------

[1] //Extending the Java Programming Language with Enumerations, Autoboxing, Enhanced for loops and Static Import//