【新・言語進化論】プロの言語仕様の読み方
第2回:nullは何型? 型、値、変数を読み解く!
著者:オープンストリーム 松下 雅和
監修者:オープンストリーム 高安 厚思
公開日:2007/11/13(火)
プリミティブ型の値の計算でなぜキャストが必要か?
intの値とlongの値を演算した場合、計算結果はlongの値となるため、int型に計算結果を格納するためにはintにキャストする必要があります。
このことについては知っている方も多いでしょう。では、なぜ計算結果はlongの値となるのでしょうか? なぜint型にキャストする必要があるのでしょうか? また、その理由を知っているでしょうか?
このような時に言語仕様を読むと謎が解けるのです。
言語仕様の「4.2.2 Integer Operations(整数演算)」には、「If an integer operator other than a shift operator has at least one operand of type long, then the operation is carried out using 64-bit precision, and the result of the numerical operator is of type long. If the other operand is not long, it is first widened (§5.1.5) to type long by numeric promotion (§5.6).(訳:シフト演算子以外の整数演算子のオペランドのいずれかがlong型である場合、演算は64ビットの精度で行われ、数値演算子の演算結果はlong型になる。この際、他方のオペランドがlong型でなければ数値の格上げ変換によってlong型へとワイドニング変換される)」とあります。
計算結果がlongの値となる理由は、intの値とlongの値を演算した場合、intの値は自動的にlong型へと変換され(プリミティブ型のワイドニング変換)、longの値同士の演算として処理されていたからです。
また、longの値をint型に格納するためには、「5.5 Casting Conversion(キャスト変換)」にあるように、キャスト変換によりプリミティブ型のナローイング変換を行う必要があります。さっきの疑問はこれで解決したでしょう。
(画像をクリックすると別ウィンドウに拡大図を表示します)
アンボクシング変換でNullPointerExceptionが発生する
Java 5から導入されたアンボクシング変換は、参照型の値を対応するプリミティブ型の値に変換する機能です(Integer型からint型など)。非常に強力な機能ですが、null参照に対してアンボクシング変換が要求された場合、NullPointerExceptionが発生してしまいます。
NullPointerExceptionが発生する原因は何でしょうか?
言語仕様の「5.1.8 Unboxing Conversion(5.1.8 アンボクシング変換)」には「If r is a reference of type Integer, then unboxing conversion converts r into r.intValue()(訳:rがInteger型の参照である場合、アンボクシング変換によってrはr.intValue()へと変換される)」とあります。
また「If r is a reference of type Long, then unboxing conversion converts r into r.longValue()(訳:rがLong型の参照である場合、アンボクシング変換によってrはr.longValue()へと変換される)」とあります。
参照型の値をプリミティブ型の値に変換する際は、Integer型の参照であればintValue()、Long型の参照であればlongValue()など、プリミティブ型の値を取得するメソッドが内部で呼ばれているのです。
null参照に対してはこのようなメソッドは実行できないため、「If r is null, unboxing conversion throws a NullPointerException(訳:rがnullの場合、アンボクシング変換によってNullPointerExceptionがスローされる)」と説明されています。これがNullPointerExceptionの発生する原因です。
ヒープ汚染の可能性
Java 5からジェネリックの仕様が追加され、クラスやインターフェース名に実型引数を指定したパラメータ化型(List<String>など)が利用可能となりました。これにともない、実型引数を指定しない従来の記述方法(Listなど)は未加工型と呼ばれるようになりました。
しかし、パラメータ化型の変数が、そのパラメータ化型のものでないオブジェクトを参照してしまう可能性は存在します。
例えば、図のようなコードを記述した場合、List<String>として宣言されたstringListが、実際にはList<Integer>の値を参照することになってしまいます。こういった状況は「ヒープ汚染」と呼ばれ、言語仕様の「4.12.2.1 Heap Pollution(ヒープ汚染)」で説明されています。
では、さらに深堀りして言語仕様の本質を見ていきましょう。 次のページ