Boolean.valueOf()がNullPointerExceptionを生成することがある


115

私はこのコードを持っています:

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
    }
}

私の問題は、テスト4がをスローするのに対して、テスト3が正常に機能する(印刷さfalseれて生成されないNullPointerException)理由が理解できないことです。テスト12でわかるように、とはとです。NullPointerExceptionnullmodifiedItems.get("item1")null

Java 7と8の動作は同じです。


modifiedItems.get( "item1")これはnullです。あなたはそれを知っていますが、これをvalueOfに渡してもNPEにならないと思いますか?
Stultuske 2017年

16
@Stultuske:null同じ関数にリテラルを渡す上記の2行だけではNPEが生成されないので、これは有効な質問です。それには十分な理由がありますが、一見すると確かに混乱します:-)
psmears

25
私が感銘を受けた。これは、私が長年見てきた中で最も興味深いヌルポインター例外の質問です。
candied_orange 2017年

@Jeroenこれはその質問の説明ではありません。ボックス化解除が2つの問題に共通していることは事実ですが、ここでは比較が行われていません。この質問の重要な点は、過負荷の解決方法が原因で発生することです。これ==は、適用方法とはかなり異なります。
アンディターナー

回答:


178

どのオーバーロードが呼び出されているかを注意深く調べる必要があります。

  • Boolean.valueOf(null)を呼び出していBoolean.valueOf(String)ます。これはNPE、nullパラメータが指定されていても、スローしません。
  • Boolean.valueOf(modifiedItems.get("item1"))呼び出しているBoolean.valueOf(boolean)ので、modifiedItemsの値は型であるBooleanアンボクシング変換を必要とします。ためmodifiedItems.get("item1")nullはない-それは、その値のアンボクシングであるBoolean.valueOf(...)- NPEをスロー。

呼び出されるオーバーロードを決定するためのルールはかなり複雑ですが、おおよそ次のようになります。

  • 最初のパスでは、ボックス化/ボックス化解除(または可変アリティメソッド)を許可せずに、メソッドの一致が検索されます。

    • はのnull受け入れ可能な値ですStringが、このパスbooleanBoolean.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つのパスがこれらのケースに一致したため、ここでは関係ありません)


3
Boolean.valueOf(modifiedItems.get("item1").booleanValue())代わりにソースコードで使用すると、コードがより明確になるBoolean.valueOf(modifiedItems.get("item1"))でしょうか?
CausingUnderflowsEverywhere

1
@CausingUnderflowsEverywhere本当にそうではない- .booleanValue()式に埋もれているのを確認するのは本当に難しい。2つの観察:1)auto(un)boxingは、構文の乱れを取り除くJavaの意図的な機能です。自分で行うことは可能ですが、慣用的ではありません。2)これはまったく役に立ちません-確かに問題の発生を止めることも、障害が発生したときに追加情報を提供することもありません(実行されたコードが同一であるため、スタックトレースは同一になります)。
アンディターナー

@CausingUnderflowsEverywhereツールを使用して問題を強調表示するほうがよい。たとえば、intellijは、潜在的なNPEについてここで得ます。
アンディターナー

13

以降modifiedItems.get戻るBoolean(あるないにキャストString)で使用される署名Boolean.valueOf(boolean)Booleanプリミティブにoutboxedされますboolean。一度nullそこに返され、outboxingがで失敗しますNullPointerException


11

メソッドの署名

このメソッドにBoolean.valueOf(...)は2つの署名があります。

  1. public static Boolean valueOf(boolean b)
  2. 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


不適切な表現、指摘ありがとうございます。回答はフィードバックに従って更新されます。お詫び申し上げます。書いている間、私はあなたの答えを見ていません。そして、私のものはあなたのように見えます。OPの混乱を避けるために回答を削除する必要がありますか?
Al-un

4
私のアカウントでは削除しないでください。これはゼロサムゲームではないことを覚えておいてください。人々は複数の回答に賛成投票できます(実際に投票します)。
アンディターナー、

3

アンディはすでに次の理由を非常によく説明していますNullPointerException

これは、ブールのボックス化解除が原因です。

Boolean.valueOf(modifiedItems.get("item1"))

に変換されます:

Boolean.valueOf(modifiedItems.get("item1").booleanValue())

実行時にnullのNullPointerException場合にスローされmodifiedItems.get("item1")ます。

次にNullPointerException、対応する返されたオブジェクトがnullの場合に、次のクラスをそれぞれのプリミティブにボックス化しないと例外が発生する可能性があることを、もう1つ指摘しておきます。

  1. バイト-バイト
  2. char-キャラクター
  3. float-フロート
  4. int-整数
  5. 長い-長い
  6. ショート-ショート
  7. double-ダブル

これがコードです:

    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

1
「実行時に...に変換」コンパイル時に変換されます。
アンディターナー

0

それを理解する方法は、Boolean.valueOf(null)が呼び出されたときに、Javaがnullを評価するように正確に指示されていることです。

ただし、Boolean.valueOf(modifiedItems.get("item1"))が呼び出されると、javaはブール型のオブジェクトのHashTableから値を取得するように指示されますが、ブール型を検出しません。NullPointerException例外がスローされるのは、Javaのこの部分の作成者が、この状況が、プログラムの問題であり、プログラマの注意が必要であると判断したためです。(意図しないことが起こりました。)

この場合、意図的にnullをそこに置くことを意図して宣言したことと、オブジェクトが見つかるはずだったオブジェクト(null)への参照が見つからないことをJavaが見つけることとの違いはさらに大きくなります。

この回答でNullPointerExceptionの詳細を参照してください:https : //stackoverflow.com/a/25721181/4425643


誰かがこの答えを改善するのを助けることができるなら、私はプログラマーが曖昧さなしに明確な意図で何かを書くことを指す言葉を考えていました
CausingUnderflowsEverywhere
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.