138

なぜこれが投げるのですかNullPointerException

public static void main(String[] args) throws Exception {
    Boolean b = true ? returnsNull() : false; // NPE on this line.
    System.out.println(b);
}

public static Boolean returnsNull() {
    return null;
}

これはしませんが

public static void main(String[] args) throws Exception {
    Boolean b = true ? null : false;
    System.out.println(b); // null
}

解決策は、箱から出されるのを避けるためにfalse置き換えるBoolean.FALSEことです-これは不可能です。しかし、それは問題ではありません。問題はなぜですか?特に2番目のケースのこの動作を確認するJLSの参照はありますか?nullboolean

4

4 に答える 4

95

違いは、returnsNull()メソッドの明示的な型がコンパイル時の式の静的型付けに影響を与えることです。

E1: `true ? returnsNull() : false` - boolean (auto-unboxing 2nd operand to boolean)

E2: `true ? null : false` - Boolean (autoboxing of 3rd operand to Boolean)

Java言語仕様のセクション15.25条件演算子を参照してください。:

  • E1の場合、2番目と3番目のオペランドのタイプはそれぞれBooleanbooleanであるため、この句が適用されます。

    2番目と3番目のオペランドの1つがブール型で、もう1つのオペランドの型がブール型の場合、条件式の型はブール型です。

    式のタイプは。であるbooleanため、2番目のオペランドを強制的に変換する必要がありますboolean。コンパイラーは、自動アンボックス化コードを2番目のオペランド(戻り値returnsNull())に挿入して、型にしbooleanます。もちろん、これにより、null実行時に返されたNPEが発生します。

  • E2の場合、2番目と3番目のオペランドのタイプはそれぞれ<special null type>BooleanE1!とは異なります)でbooleanあるため、特定の入力句は適用されません(go read'em!)。したがって、最後の「otherwise」句が適用されます。

    それ以外の場合、2番目と3番目のオペランドはそれぞれタイプS1とS2です。T1をS1にボクシング変換を適用した結果のタイプとし、T2をS2にボクシング変換を適用した結果のタイプとします。条件式のタイプは、キャプチャ変換(§5.1.10)をlub(T1、T2)(§15.12.2.7)に適用した結果です。

    • S1 == <special null type>§4.1を参照)
    • S2 ==boolean
    • T1 == box(S1)== ( §5.1.7<special null type>のボクシング変換リストの最後の項目を参照 )
    • T2 == box(S2)==`ブール値
    • lub(T1、T2)==Boolean

    したがって、条件式のタイプはでBooleanあり、3番目のオペランドは強制的に変換する必要がありますBoolean。コンパイラは、3番目のオペランド(false)の自動ボクシングコードを挿入します。2番目のオペランドは、のように自動アンボックス化を必要としないため、が返さE1れるときに自動アンボックス化NPEはありません。null


この質問には、同様のタイプ分析が必要です。

Java条件演算子?:結果タイプ

于 2010-10-07T14:00:20.700 に答える
28

この線:

    Boolean b = true ? returnsNull() : false;

内部的に次のように変換されます。

    Boolean b = true ? returnsNull().booleanValue() : false; 

開梱を実行します。したがって:null.booleanValue()NPEが生成されます

これは、オートボクシングを使用する際の主な落とし穴の1つです。この動作は、5.1.8JLSで実際に文書化されています

編集:開開は、(暗黙のキャストが追加された)のようなブール型の3番目の演算子によるものだと思います:

   Boolean b = (Boolean) true ? true : false; 
于 2010-10-07T13:38:57.123 に答える
17

Java言語仕様のセクション15.25から:

  • 2番目と3番目のオペランドの1つがブール型で、もう1つのオペランドの型がブール型の場合、条件式の型はブール型です。

したがって、最初の例では、最初のルールに従ってBoolean.booleanValue()に変換するためにBoolean呼び出しを試みます。boolean

2番目のケースでは、最初のオペランドがnullタイプであり、2番目のオペランドが参照タイプではないため、自動ボクシング変換が適用されます。

  • それ以外の場合、2番目と3番目のオペランドはそれぞれタイプS1とS2です。T1をS1にボクシング変換を適用した結果のタイプとし、T2をS2にボクシング変換を適用した結果のタイプとします。条件式のタイプは、キャプチャ変換(§5.1.10)をlub(T1、T2)(§15.12.2.7)に適用した結果です。
于 2010-10-07T13:41:27.430 に答える
0

この問題はバイトコードからわかります。mainのバイトコードの3行目3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z、値nullのボクシングブール値invokevirtual、メソッドjava.lang.Boolean.booleanValue、もちろんNPEをスローします。

    public static void main(java.lang.String[]) throws java.lang.Exception;
      descriptor: ([Ljava/lang/String;)V
      flags: ACC_PUBLIC, ACC_STATIC
      Code:
        stack=2, locals=2, args_size=1
           0: invokestatic  #2                  // Method returnsNull:()Ljava/lang/Boolean;
           3: invokevirtual #3                  // Method java/lang/Boolean.booleanValue:()Z
           6: invokestatic  #4                  // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
           9: astore_1
          10: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
          13: aload_1
          14: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
          17: return
        LineNumberTable:
          line 3: 0
          line 4: 10
          line 5: 17
      Exceptions:
        throws java.lang.Exception

    public static java.lang.Boolean returnsNull();
      descriptor: ()Ljava/lang/Boolean;
      flags: ACC_PUBLIC, ACC_STATIC
      Code:
        stack=1, locals=0, args_size=0
           0: aconst_null
           1: areturn
        LineNumberTable:
          line 8: 0
于 2016-03-24T01:31:33.823 に答える