私はこのコードを持っています:
package tests;
import Java.util.Hashtable;
public class Tests {
public static void main(String[] args) {
Hashtable<String, Boolean> modifiedItems = new Hashtable<String, Boolean>();
System.out.println("TEST 1");
System.out.println(modifiedItems.get("item1")); // Prints null
System.out.println("TEST 2");
System.out.println(modifiedItems.get("item1") == null); // Prints true
System.out.println("TEST 3");
System.out.println(Boolean.valueOf(null)); // Prints false
System.out.println("TEST 4");
System.out.println(Boolean.valueOf(modifiedItems.get("item1"))); // Produces NullPointerException
System.out.println("FINISHED!"); // Never executed
}
}
私の問題は、なぜテストがうまく機能するのか理解できないことです(false
を出力し、NullPointerException
を生成しません)テスト4NullPointerException
をスローします。テストでわかるように、1と2、null
とmodifiedItems.get("item1")
は等しく、null
です。
動作はJava 7と8で同じです。
どのオーバーロードが呼び出されているかを注意深く調べる必要があります。
Boolean.valueOf(null)
が呼び出しています Boolean.valueOf(String)
。これは、nullパラメーターを指定しても、NPE
をスローしません。Boolean.valueOf(modifiedItems.get("item1"))
は、 Boolean.valueOf(boolean)
を呼び出しています。これは、modifiedItems
の値がBoolean
型であり、ボックス化解除変換が必要だからです。 modifiedItems.get("item1")
はnull
であるため、NPEをスローするのはBoolean.valueOf(...)
ではなく、その値のボックス化解除です。どのオーバーロードが呼び出されるかを決定するためのルールは pretty hairy ですが、おおよそ次のようになります。
最初のパスでは、ボックス化/ボックス化解除を許可せずにメソッド一致が検索されます(可変アリティメソッドも)。
null
はString
の許容値ですが、boolean
ではないため、このパスではBoolean.valueOf(null)
がBoolean.valueOf(String)
と一致します。Boolean
は、Boolean.valueOf(String)
またはBoolean.valueOf(boolean)
のいずれにも受け入れられないため、Boolean.valueOf(modifiedItems.get("item1"))
のこのパスで一致するメソッドはありません。2番目のパスでは、メソッドの一致が検索され、ボクシング/アンボクシングが可能になります(ただし、可変アリティメソッドは許可されません)。
Boolean
はboolean
にアンボックス化できるため、このパスでBoolean.valueOf(boolean)
がBoolean.valueOf(modifiedItems.get("item1"))
に一致します。しかし、それを呼び出すには、コンパイラーがアンボックス化変換を挿入する必要があります:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
(変数アリティメソッドを許可する3番目のパスがありますが、最初の2つのパスがこれらのケースに一致したため、ここでは関係ありません)
modifiedItems.get
はBoolean
(notがString
にキャスト可能)を返すため、署名は使用されるのは Boolean.valueOf(boolean)
です。ここで、Boolean
はプリミティブboolean
にアウトボックス化されています。 null
がそこに返されると、送信ボックスはNullPointerException
で失敗します。
メソッドBoolean.valueOf(...)
には2つのシグネチャがあります。
public static Boolean valueOf(boolean b)
public static Boolean valueOf(String s)
modifiedItems
値はBoolean
です。 Boolean
をString
にキャストできないため、最初の署名が選択されます
あなたの声明で
Boolean.valueOf(modifiedItems.get("item1"))
として読むことができます
Boolean.valueOf(modifiedItems.get("item1").booleanValue())
ただし、modifiedItems.get("item1")
はnull
を返すため、基本的には
null.booleanValue()
明らかにNullPointerException
につながります
Andyが既にNullPointerException
の理由を非常によく説明しているように:
これは、ブールのボックス化解除によるものです。
Boolean.valueOf(modifiedItems.get("item1"))
に変換されます:
Boolean.valueOf(modifiedItems.get("item1").booleanValue())
実行時に、modifiedItems.get("item1")
がnullの場合、NullPointerException
をスローします。
ここで、対応する返されたオブジェクトがnullの場合、それぞれのプリミティブへの次のクラスのボックス化解除によりNullPointerException
例外が発生する可能性があるという点をもう1つ追加します。
コードは次のとおりです。
Hashtable<String, Boolean> modifiedItems1 = new Hashtable<String, Boolean>();
System.out.println(Boolean.valueOf(modifiedItems1.get("item1")));//Exception in thread "main" Java.lang.NullPointerException
Hashtable<String, Byte> modifiedItems2 = new Hashtable<String, Byte>();
System.out.println(Byte.valueOf(modifiedItems2.get("item1")));//Exception in thread "main" Java.lang.NullPointerException
Hashtable<String, Character> modifiedItems3 = new Hashtable<String, Character>();
System.out.println(Character.valueOf(modifiedItems3.get("item1")));//Exception in thread "main" Java.lang.NullPointerException
Hashtable<String, Float> modifiedItems4 = new Hashtable<String, Float>();
System.out.println(Float.valueOf(modifiedItems4.get("item1")));//Exception in thread "main" Java.lang.NullPointerException
Hashtable<String, Integer> modifiedItems5 = new Hashtable<String, Integer>();
System.out.println(Integer.valueOf(modifiedItems5.get("item1")));//Exception in thread "main" Java.lang.NullPointerException
Hashtable<String, Long> modifiedItems6 = new Hashtable<String, Long>();
System.out.println(Long.valueOf(modifiedItems6.get("item1")));//Exception in thread "main" Java.lang.NullPointerException
Hashtable<String, Short> modifiedItems7 = new Hashtable<String, Short>();
System.out.println(Short.valueOf(modifiedItems7.get("item1")));//Exception in thread "main" Java.lang.NullPointerException
Hashtable<String, Double> modifiedItems8 = new Hashtable<String, Double>();
System.out.println(Double.valueOf(modifiedItems8.get("item1")));//Exception in thread "main" Java.lang.NullPointerException
これを理解する方法は、Boolean.valueOf(null)
が呼び出されたとき、Javaがnullを評価するように正確に指示されたときです。
ただし、Boolean.valueOf(modifiedItems.get("item1"))
が呼び出されると、JavaはBoolean型のHashTableから値を取得するように指示されますが、代わりに行き止まりを検出するBoolean型は見つかりません(null)ブール値を想定していましたが。 NullPointerException例外がスローされるのは、Javaのこの部分の作成者が、この状況がプログラマーの注意を必要とするプログラムの問題のインスタンスであると判断したためです。 (意図しない何かが起こりました。)
この場合、nullが存在することを意図的に宣言することと、オブジェクトが見つからないオブジェクト(null)への参照が見つからないことをJavaで見つけることとの違いが大きくなります。
この回答のNullPointerExceptionの詳細を参照してください: https://stackoverflow.com/a/25721181/442564