M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

ラッパークラスインスタンスのオートアンボクシングについて

久々の更新も職場関連。気になっていたものの、些末なことでもあり、また実際忙しくてやっている余裕もなかったことを確認した結果をメモしておきます。

Javaのプリミティブ型に対応するラッパークラスは、いくつかの制約はあるものの、必要な場面でプリミティブ型を自動で取得して処理するようになっています。

これはもちろん「ラッパークラスが…するようになっている」というのは、正確に言えば「Javaコンパイラーによって…するようにコードが追加される」というこです。

ところでこれを前提にして安易なコーディングを行うと、同じ値の数同士の比較なのに==演算子がtrueを返さなかったり、数値同士の比較でNullPointerExceptionがスローされるという、思わぬ事態に見舞われることがあります。

もちろんそこで起きていることは至極当然のことです。

public class WrapperStudy {
	public static void main(String[] args) {
		
		final int int1985 = 1985;
		final Integer wrapped1985 = Integer.valueOf(1985);
		final Integer wrapped1985b = 1985;
		final Integer wrapped1985c = null;
		
		// ①
		System.out.println("int1985 == wrapped1985 // --> " + (int1985 == wrapped1985));
		
		// ②
		System.out.println("wrapped1985 == int1985 // --> " + (wrapped1985 == int1985));
		
		// ③
		System.out.println("wrapped1985b == int1985 // --> " + (wrapped1985b == int1985));
		
		// ④
		System.out.println("wrapped1985 == wrapped1985b // --> " + (wrapped1985 == wrapped1985b));
		
		try{
			System.out.println("int1985 == wrapped1985c // --> " + (int1985 == wrapped1985c));
		}catch(NullPointerException e){
			System.out.print("int1985 == wrapped1985c // --> " + e.toString());
		}	
	}
}

上記のコードを実行すると、次のような出力が得られます。

int1985 == wrapped1985 // --> true
wrapped1985 == int1985 // --> true
wrapped1985b == int1985 // --> true
wrapped1985 == wrapped1985b // --> false
int1985 == wrapped1985c // --> java.lang.NullPointerException
  1. プリミティブ(左)とラッパー(右)の比較では、当然ラッパー側がアンボクシングされて、プリミティブ同士の値比較が実施されます。結果はtrueです。
  2. ラッパー(左)とプリミティブ(右)の比較でも、ラッパー側がアンボクシングされます。左側にラッパーがあるからといって、右側のプリミティブがオートボクシングされるわけではないのです。
  3. プリミティブとラッパーの比較における値比較の原則は、ラッパーの生成方法とは関係ありません。
  4. ラッパー同士の比較は、通常のオブジェクト同様、参照の値比較が実施されます。したがって2つの参照がヒープの同じ位置を指すモノでないかぎり結果はfalseです。
  5. アンボクシングは何もJava VMの魔法で実現しているわけではなく、xxxValue()系メソッドの呼び出しにより実現されています。したがってnullが格納されたラッパー変数とプリミティブを比較しようとすると実行時例外がスローされます。

些細なことながら一度網羅的に(といってもたいした数ではありませんが)確かめてみたかった部分でした。

ところで、Integerクラスでは一定の大きさの数までは、リソース節約のために同じIntegerインスタンスが使い回されます。したがって次のような処理を書くと、

		for(int i = 0; i < Integer.MAX_VALUE; i ++){
			Integer a = i;
			Integer b = i;
			if(a != b){
				System.out.printf("%d == %d // --> false", a, b);
				break;
			}
		}

次のような出力が得られます。

128 == 128 // --> false

Stringクラスインスタンスにも似たような問題がありますが、Integerのこのような性質によって、前述の値比較への注意が鈍ってしまうという問題があります。