ボックス化
ボックス化(boxing)[1]とは、プログラミング言語において値型をオブジェクト型(参照型)に変換すること。逆に、ボックス化されたオブジェクトを値型に戻すことをボックス化解除(unboxing)[2]と呼ぶ。
概要
編集Javaや.NET Frameworkなどの環境においては、値型(Javaではプリミティブ型[3]がこれに相当する)と参照型という根本的に異なる二種類の型が存在する。参照型のインスタンスはヒープ上の独立した領域に確保される。値型は文脈によって確保される場所は異なるものの、いずれにせよメモリ上に連続的に確保される(例えば、ローカル変数として宣言された場合はスタック上に確保され、参照型のメンバとして宣言された場合は参照型の一部として確保される)。
このように値型と参照型とは根本的に性質の異なるものであるが、さまざまな理由により、値型を参照型に型変換して運用することが必要となる場合がある。Javaにおいて整数型や浮動小数点数型といったプリミティブ型はオブジェクト(java.lang.Object
派生型)ではなく、配列以外のコレクションにプリミティブ型を直接格納することができないため、一度参照型(java.lang.Object
派生型)でラッピング(包含)してから格納する必要がある。このラッピング処理をボックス化と呼ぶ。各プリミティブ型をラップするための専用クラス(プリミティブラッパークラス)は標準Javaクラスライブラリに用意されているため、プログラマが自ら定義する必要はない。
次はJavaによる例である。プリミティブ型のひとつであるint
型の値を、対応するプリミティブラッパークラスであるjava.lang.Integer
型のオブジェクトにボックス化し、さらにそれをint
型にボックス化解除している。ボックス化により、値はラッパークラス内のカプセル化されたフィールドにて保持されるが、ラッパークラスの使用者は詳細を知る必要はない。
int iv1 = 100;
Integer iw = new Integer(iv1); // ボックス化。
int iv2 = iw.intValue(); // ボックス化解除。
一見単純な型変換のように見えるが、ボックス化においてはヒープ上に新しく領域を確保し、そこに値型(プリミティブ型)のデータをコピーするという操作が行われているため注意が必要である。頻繁なボックス化の発生は性能の低下を引き起こす。一方、ボックス化解除ではデータのコピーが行われるだけであり、ヒープに領域を確保する必要がないのでパフォーマンスコストは比較的小さい。
なお、ゼロ近傍の整数などのよく使われる数値に対して使いまわせるように事前生成されている、ラッパーオブジェクトのキャッシュを優先的に利用できるようにするため、通例Integer.Integer(int)
コンストラクタ[4]よりもInteger.valueOf(int)
メソッドなどの使用が好ましい。
int iv1 = 100;
Integer iw = Integer.valueOf(iv1); // ボックス化。
関数型言語におけるボックス化
編集関数型言語でも用いられることがあるが、オブジェクト指向のそれとの関連性はまったくない。ボックス型は単なる間接参照であり、Javaのプリミティブ型に相当する型は非ボックス型(Unboxed type)やプリミティブ型と呼ばれ、直接参照される。関数型言語のひとつであるHaskellでは、そのデファクトスタンダードな処理系であるGHCに対してコンパイラオプションを指定し、なおかつ非ボックス型であることを随所で明示しない限り、非ボックス型を用いることができない。にもかかわらず、知らず識らずのうちに利用している、馴染み深い型でもある。IO型など、副作用を表現する型がその典型例である。
自動ボックス化
編集前述のボックス化・ボックス化解除の操作を暗黙的に行なうことを、自動ボックス化(autoboxing)[5]および自動ボックス化解除(auto-unboxing)と呼ぶ。
自動ボックス化はJava SE 5 (J2SE 5.0, Java 1.5) で追加され、JSR 201で宣言されている。自動ボックス化がサポートされた処理系では、対となる逆の操作、自動ボックス化解除もサポートされる。
以下のコードは自動ボックス化・自動ボックス化解除の例である。
int iv1 = 100;
Integer iw = iv1; // ボックス化。
int iv2 = iw; // ボックス化解除。
Javaコンパイラによって、プリミティブ型とプリミティブラッパークラス間の暗黙変換は、前述のようなボックス化・ボックス化解除を実行するコードに展開される。
.NET
編集.NETにおいては、概念(型システム)上、値型も含めすべての型はSystem.Object
型(これは参照型である)の派生型であるため、この型への暗黙的なアップキャストを行なうことができる。すべての値型はSystem.ValueType
クラスから派生し、その基底クラスであるSystem.Object
への暗黙的なボックス化が可能となっている(Javaの自動ボックス化に相当)。一方、ボックス化解除はダウンキャストにより明示的に実行する必要がある。
C#の例を示す。
int iv1 = 100;
object iw = iv1; // 暗黙的なボックス化。
int iv2 = (int)iw; // ボックス化解除。
明示的なボックス化も可能ではあるが、通例使われない[6]。
object iw = (object)iv1; // 明示的なボックス化。
コレクションの例
編集Javaでは、配列以外のコレクション(java.util.ArrayList
やjava.util.LinkedList
など)には参照型のみ追加することができる。プリミティブ型のデータをそのまま要素として追加することはできない。
そこで、プリミティブ型のデータは対応するプリミティブラッパークラスのオブジェクトにボックス化して追加し、取り出すときにはダウンキャストしてからボックス化解除する。
import java.util.*;
...
List list = new ArrayList(); // Object 型のみ格納できる。
//list.add(5); // Java 1.4 以前ではコンパイルエラー。
list.add(Integer.valueOf(5)); // Integer 型にボックス化してリストに追加する。
Integer iw = (Integer)list.get(0); // Object 型から Integer 型にダウンキャスト。
int iv = iw.intValue(); // Integer 型からボックス化解除。
Java 1.5以降ではジェネリクスおよび自動ボックス化をサポートするので、プリミティブ型のデータもあたかもオブジェクトのように扱うことができる。ただしコレクションには参照型のみ追加できること、またボックス化・ボックス化解除が実行されることには変わりない。Javaのジェネリクスは型安全であることが保証されているだけであり、ジェネリクスを使わない場合と比べてパフォーマンス上の優位性はない[7]。
List<Integer> list = new ArrayList<Integer>(); // Integer 型のみ格納できる。
list.add(5); // int 型の値から自動的に Integer 型のオブジェクトにボックス化される。
int iv = list.get(0); // Integer 型のオブジェクトから自動的に int 型の値にボックス化解除される。
.NET
編集.NET 1.1まではジェネリクスをサポートしなかったため、配列以外のコレクションにはJava同様にボックス化・ボックス化解除を利用する必要があった。
using System.Collections;
...
IList list = new ArrayList(); // object 型のみ格納できる。
list.Add(5); // object 型にボックス化してリストに追加する。
object iw = list[0];
int iv = (int)iw; // object 型から int 型にダウンキャストしてボックス化解除。
一方、.NET 2.0 (C# 2.0) 以降のジェネリクスでは値型を直接扱うことができ、Javaと違ってボックス化・ボックス化解除は発生しない。
using System.Collections.Generic;
...
IList<int> list = new List<int>(); // int 型のみ格納できる。
list.Add(5);
int iv = list[0];
脚注
編集- ^ カナ表記で「ボクシング」とも。
- ^ カナ表記で「アンボクシング」とも。
- ^ 「原始型」とも。
- ^ Java 9以降は
@Deprecated
すなわち廃止予定としてマークされている。 - ^ カナ表記で「オートボクシング」とも。
- ^ ボックス化とボックス化解除 (C# プログラミング ガイド) | Microsoft Docs
- ^ Autoboxing | Oracle Java Documentaiton
関連項目
編集- 型変換
- Java Community Process - Java技術の標準化プロセス
外部リンク
編集- JSR 201 Extending the Java Programming Language with Enumerations, Autoboxing, Enhanced for loops and Static Import (J2SE 5.0)