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?
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
iboolean
, 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 naboolean
. Kompilator wstawia kod automatycznego rozpakowywania do drugiego operandu (zwracana wartośćreturnsNull()
), aby nadać mu typboolean
. To oczywiście powoduje NPE znull
zwracanego w czasie wykonywania. -
Dla E2, typy 2. i 3. operandów to
<special null type>
(nie {[2] } jak w E1!) iboolean
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 naBoolean
. Kompilator wstawia kod auto-boxingu dla trzeciego operandu (false
). 2. operand nie wymaga automatycznego unboxingu jak wE1
, więc nie ma automatycznego unboxingu NPE gdy zwraca sięnull
. - S1 = =
To pytanie wymaga podobnej analizy typu:
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
Edit: uważam, że unboxing jest spowodowany tym, że trzeci operator jest typu boolean, np.]}
Boolean b = (Boolean) true ? true : false;
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).
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
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