4-5-1: 原始型と wrapperクラス


・ Javaのプログラムの中で取り扱われるデータは、次のように分類することができます。

  1. 原始型(primitive type)
  2. クラスのオブジェクト
  3. 上記のデータの配列

まず原始型(primitive type)のデータについて先に触れておきましょう。 数値(文字コード値を含む)や論理値を表す単純なデータです。 たとえば、数値には次のような型があり、そのサイズも決まっています。 原始型の名称や意味は Cのものをほとんど踏襲していますが、 若干の違いがあります。

Javaの数値型
byte short char int long float double
1バイト 2バイト 2バイト 4バイト 8バイト 4バイト 8バイト

論理値のデータを扱う型が boolean型です。 値は true もしくは false です。 true と false は Javaの予約語です。 数値型のデータにキャストしたりはできません。 void型は実体が存在しない型です。 メソッドの返値が存在しないことを示すために用います。

・ 原始型はクラスのオブジェクトではなく「裸のデータ」です。 このようなデータの存在を許すということは、 純粋なオブジェクト指向の立場からは多少逸脱しています。 ただし、Javaの原始型は決してなし崩し的に導入されたものではありません。 その証拠に、 JDKの java.langパッケージには9個の原始型のそれぞれに対応して9個のクラスが ちゃんと用意されています。これらは「ラッパ(wrapper)クラス」と呼ばれます。 原始型のデータを「包み込む」ようにして覆い隠し、 外部とのやりとりの機能を提供してくれるからです。 ラッパクラスの名称は下記のように原始型にほぼ対応します。 (ただし char に対応するクラス名は Character、 int に対応するクラス名は Integerです。)
厳密なオブジェクト指向の立場に立って「美しいプログラム」を組みたいと思ったら、 これらのラッパクラスを使用すればいいでしょう。 ただし、それは必ずしも現実的な選択ではありません。 数値や論理型のデータは非常に頻繁に用いますから、 実際にソースコードを書く立場になれば原始型を用いた方がはるかに効率的です。 また開発されたアプリケーションの実行段階でも、明らかに効率面で有利です。 Javaは理論的な純粋さよりも現実面を優先させる選択をしたと言えるでしょう。

原始型とそのラッパクラス
ラッパクラス名 Byte Short Character Integer Long Float Double Boolean Void
対応する原始型 byte short char int long float double boolean void

なお上記のラッパクラスは、 原始型を取り扱うためのさまざまな便利な機能も提供してくれます。 たとえば Intergerクラスは文字列を数値に変換したり、 16進表現を 10進表現に変換したりする機能を提供します。 Javaでラッパクラスを利用するのは、 むしろそうした便利な機能を呼び出すための場合が多いでしょう。

・ さて、原始型についてもう一つ説明しなければならない重要なことは、 データの寿命の問題です。 原始型のデータのためのメモリーは、 それがプログラム内で宣言された時点で確保されます。 それが宣言されたメソッドもしくはブロックの処理が終了すると、 自動的にその寿命は尽きます。 メソッド(コンストラクタも含む)の引数に与えられた原始型のデータは、 メソッドが呼び出されたると同時にメモリー領域が確保され、 メソッドの呼び出し側で与えられた値がコピーされます。 その寿命はやはりメソッドの処理が終わるまでです。 (呼び出し側のオリジナルのデータは消滅しません。) またクラスのフィールドとして宣言された原始型のデータは、 そのオブジェクトが生成された時にメモリが確保され オブジェクトと同じ寿命を持ちます。 (ただし static宣言されたフィールドは別です。)
以上のルールは比較的理解しやすいものでしょう。 ソースプログラム内のブロック構造が、 そのままデータの寿命に対応しているからです。 また原始型のデータの寿命に対応するブロックは、 変数名が有効な範囲とも完全に一致しています。


public class PrimitiveData {
 
     public static int w;  // w の寿命はクラス JVMにロードされている間
     public int x;         // x の寿命はオブジェクトが消滅するまで
 
     public void calulate( int y ){  // y の寿命はメソッドを抜けるまで
 
          int z;    // z の寿命はメソッドを抜けるまで
          for( int i=0; i<10; i++ ) { // i の寿命はこのブロックを抜けるまで
               z += i;
          }
     }