質問

なぜこれが投げるのですか 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
}

?

解決策は、交換する方法です falseBoolean.FALSE 避けるために null ボックス化されていません boolean - それは不可能です。しかし、それは問題ではありません。質問は どうして? JLSには、この動作、特に2番目のケースを確認する参照はありますか?

役に立ちましたか?

解決

違いは、明示的なタイプのものです 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つが型ブール値であり、他のオペランドのタイプが型ブール値である場合、条件付き式のタイプはブール値です。

    式のタイプはあるので boolean, 、2番目のオペランドは強制されなければなりません boolean. 。コンパイラは、2番目のオペランドに自動不動のコードを挿入します( returnsNull())タイプにするため boolean. 。これはもちろん、からNPEを引き起こします null 実行時に返されます。

  • E2の場合、2番目と3番目のオペランドのタイプは <special null type> (いいえ Boolean E1のように!)そして boolean それぞれ、特定のタイピング条項が適用されません('emを読んでください!)、したがって、最終的な「それ以外の」節が適用されます。

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

    • S1 == <special null type> (見る §4.1)
    • S2 == boolean
    • t1 == box(s1)== <special null type> (ボクシング変換のリストの最後の項目を参照してください §5.1.7)
    • t2 == box(s2)== `boolean
    • LUB(T1、T2)== Boolean

    したがって、条件付き式のタイプはです Boolean そして、3番目のオペランドは強制されなければなりません Boolean. 。コンパイラは、3番目のオペランドの自動ボクシングコードを挿入します(false)。 2番目のオペランドでは、Auto-Unboxingが必要ではありません E1, 、したがって、自動不動のNPEはありません null 返されます。


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

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

他のヒント

この線:

    Boolean b = true ? returnsNull() : false;

内部的に変換されます:

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

ボックス化を実行するには。したがって: null.booleanValue() NPEが生成されます

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

編集:アンボクシングは、3番目のオペレーターがブール型タイプの(暗黙のキャストを追加)によるものだと思います。

   Boolean b = (Boolean) true ? true : false; 

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

  • 2番目と3番目のオペランドの1つが型ブール値であり、他のオペランドのタイプが型ブール値である場合、条件付き式のタイプはブール値です。

したがって、最初の例は呼び出そうとします Boolean.booleanValue() 変換するために Booleanboolean 最初のルールに従って。

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

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

この問題はBYTEコードから見ることができます。メインのバイトコードの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
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top