Booleans, operatory warunkowe i autoboxing

Dlaczego ten rzut 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;
}

While this doesn ' t

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

?

Rozwiązaniem jest by the way to replace false by Boolean.FALSE to avoid null being unboxed to boolean --which is not possible. Ale to nie jest pytanie. Pytanie brzmi dlaczego ? Czy są jakieś odniesienia w JLS, które potwierdzają to zachowanie, zwłaszcza w drugim przypadku?

Author: BalusC, 2010-10-07

4 answers

Różnica polega na tym, że jawny Typ metody returnsNull() wpływa na statyczne typowanie wyrażeń w czasie kompilacji:

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

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

Patrz specyfikacja języka Java, sekcja 15.25 Operator warunkowy ? :

  • Dla E1 typy 2. i 3. operandów to odpowiednio Boolean i boolean, więc zastosowanie ma ta klauzula:

    Jeśli jeden z drugich i trzecich operandów jest typu boolean, A Typ drugiego jest typu Boolean, wtedy typem wyrażenia warunkowego jest boolean.

    Ponieważ typem wyrażenia jest boolean, 2. operand musi być wymuszony na boolean. Kompilator wstawia kod automatycznego rozpakowywania do drugiego operandu (zwracana wartość returnsNull()), aby nadać mu typ boolean. To oczywiście powoduje NPE z null zwracanego w czasie wykonywania.

  • Dla E2, typy 2. i 3. operandów to <special null type> (nie {[2] } jak w E1!) i boolean odpowiednio, więc nie ma specyficznych klauzula typing applies (go read ' em!), więc ostateczna klauzula "inaczej" ma zastosowanie:

    Inaczej, drugi i trzeci operand są odpowiednio typu S1 i S2. Niech T1 będzie typem wynikającym z zastosowania konwersji boksu do S1, a t2 będzie typem wynikającym z zastosowania konwersji boksu do S2. Typ wyrażenia warunkowego jest wynikiem zastosowania konwersji przechwytywania (§5.1.10) do lub (T1, T2) (§15.12.2.7).

    • S1 = = <special null type> (patrz §4.1)
    • S2 = = boolean
    • T1 = = box (S1) = = <special null type> (patrz ostatnia Pozycja na liście konwersji boksu w §5.1.7)
    • T2 = = box (S2) == `Boolean
    • lub (T1, T2) = = Boolean

    Tak więc typem wyrażenia warunkowego jest Boolean, a trzeci operand musi być wymuszony na Boolean. Kompilator wstawia kod auto-boxingu dla trzeciego operandu (false). 2. operand nie wymaga automatycznego unboxingu jak w E1, więc nie ma automatycznego unboxingu NPE gdy zwraca się null.


To pytanie wymaga podobnej analizy typu:

Java operator warunkowy ?: Typ wyniku

 88
Author: Bert F,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:17

Linia:

    Boolean b = true ? returnsNull() : false;

Jest wewnętrznie przekształcone na:

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

Aby wykonać unboxing; tak więc: null.getBoolean() Da NPE

Jest to jedna z głównych pułapek podczas korzystania z autoboxingu. Jest to udokumentowane w 5.1.8 JLS

Edit: uważam, że unboxing jest spowodowany tym, że trzeci operator jest typu boolean, np.]}

   Boolean b = (Boolean) true ? true : false; 
 22
Author: jjungnickel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 13:46:06

Ze specyfikacji języka Java, sekcja 15.25:

  • Jeśli jeden z drugiego i trzeciego operands jest typu boolean i Typ drugiej jest typu Boolean, następnie Typ warunkowego wyrażenie jest logiczne.

Pierwszy przykład próbuje wywołać Boolean.booleanValue() w celu konwersji Boolean na boolean zgodnie z pierwszą regułą.

W drugim przypadku pierwszy operand jest typu null, gdy drugi nie jest referencją Typ, więc zastosowana jest konwersja autoboxingu:

  • inaczej, drugi i trzeci operandami są typy S1 i S2 odpowiednio. Niech T1 będzie typem, który wyniki stosowania boksu konwersja na S1 i niech T2 będzie Typ wynikający z zastosowania boksu konwersja na S2. Rodzaj wyrażenie warunkowe jest wynikiem stosowania konwersji przechwytywania (§5.1.10) do lub (T1, T2) (§15.12.2.7).
 16
Author: axtavt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 16:32:18

Widzimy ten problem z kodu bajtowego. W linii 3 kodu bajtowego main, 3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z, Boks Boolean wartości null, invokevirtual Metoda java.lang.Boolean.booleanValue, rzuci NPE oczywiście.

    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
 0
Author: Yanhui Zhou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-24 01:31:33