2014-09-19 9 views
15

Otrzymuję NullPointerException w jednym przykładzie poniżej, podczas gdy jego odpowiednik działa gładko.Dlaczego Ternary Operacja daje nullpointer, gdy jego odpowiednik ifelse nie?

public static void main(String[] args){ 
    System.out.println(withTernary(null, null)); //Null Pointer 
    System.out.println(withIfElse(null, null)); //No Exception 
} 

private static Boolean withTernary(String val, Boolean defVal){ 
    return val == null ? defVal : "true".equalsIgnoreCase(val); 
} 

private static Boolean withIfElse(String val, Boolean defVal){ 
    if (val == null) return defVal; 
    else return "true".equalsIgnoreCase(val); 
} 

Online version

Online version with the lines in main reversed, który wyprowadza null z withIfElse i nie w withTernary.

Używam następujących wersji java

java version "1.6.0_65" 
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-462-11M4609) 
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-462, mixed mode) 
+1

To jest duplikatem. – Raedwald

+2

@Raedwald: Z czego? Dlaczego nie oznaczyć go jako takiego? –

+2

To jest naprawdę cholernie mylące. To bardzo ważna sprawa w JLS ... – nneonneo

Odpowiedz

9

Oto odpowiedni cytat z the spec (§15.25.2):

logiczne wyrażenia warunkowe są wyrażeniami standalone (§15.2).

Typ logicznej ekspresji warunkowej jest określona następująco:

  • przypadku drugi i trzeci argumenty są zarówno typu Boolean wyrażenie warunkowym typu Boolean.

  • W przeciwnym razie wyrażenie warunkowe ma typ boolean.

Dlatego typ ogólnej ekspresji jest uważany za boolean, a wartość Boolean jest autounboxed, powodując NullPointerException.


Jak wspomniano w komentarzach, dlaczego następujące wyjątki nie podnoszą wyjątku?

return val == null ? null : "true".equalsIgnoreCase(val); 

Dobrze, powyższy fragment spec konkretnie odnosi się tylko do logicznych wyrażeń warunkowych, które są określone here (§15.25):

Jeśli zarówno drugi i trzeci wyrażenia operand są wyrażenia boolowskie , wyrażenie warunkowe jest wyrażeniem warunkowym boolowskim.

Do celów klasyfikacji warunkowego, następujące wyrażenia mają wyrażenia logiczne:

  • Wyrażenie autonomicznej formy (§15.2), która jest typu boolean lub Boolean.

  • Wyrażone w nawiasach wyrażenie boolean().

  • Wyrażenie tworzenia instancji klasy (§15.9) dla klasy Boolean.

  • Wyrażenie sposób wywołania (§15.12), w których wybrana najbardziej specyficzny sposób (§15.12.2.5) ma typ wrócimy boolean lub Boolean.
    (Zauważ, że dla metody rodzajowe, jest to typ przed instancji argumenty typu metody Użytkownika).

  • boolean wyrażenie warunkowe.

Od null nie jest wyrażenie logiczne, ogólne wyrażenie warunkowe nie jest logiczna wyrażenie warunkowe. Odnosząc się do Tabeli 15.2 (później w tej samej sekcji) widzimy, że takie wyrażenie ma typ Boolean, więc nie ma rozpakowywania i nie ma wyjątków.

+0

Następnie ten "return val == null? Null:" true ".equalsIgnoreCase (val);" powinien również zawieść, ale tak nie jest. Dobrze? – DKSRathore

+0

@DKSRathore Zaktualizowałem swoją odpowiedź, aby wyjaśnić tę sprawę. –

4

Ponieważ autounboxing. Typ Java jest typem wspólnym dla defVal i "true".equalsIgnoreCase(val). Typ pierwszego to Boolean, ale drugi boolean. Z nieznanego powodu uważa, że ​​typ wspólny będzie boolean (można znaleźć regułę w specyfikacji).

7

val == null ? defVal : "true".equalsIgnoreCase(val) - w tym wyrażeniu, trzeci argument jest boolean, a ponieważ operator trójargumentowy musi mieć jeden typ statyczny, będzie starał się unbox się null, stąd NPE. Tylko przypisanie do Boolean spowoduje ponowne utworzenie boksu. Sprawdź this.

+0

Nie znajduję NullPointer w zamian val == null? null: "true" .equalsIgnoreCase (val) ;. Czemu? – DKSRathore

+0

NPE dzieje się, gdy próbowałeś z Boolean. Kompilator próbował rozpakować "Boolean" z wartością null do "boolean". Stąd NPE. Tutaj trzeci operand ma poprawną wartość, prosty – RahulArackal

3

Można to wyjaśnić, jeśli defVal jest null.

Z wyrażeń trójskładnikowych, obie te opcje muszą być dokładnie tego samego typu, a jeśli nie jakiś przymus jest stosowany:

JLS 15.25 "Conditional operator ? :" mówi:

Jeśli jeden z drugim i trzecim operandów jest prymitywny typu T, a drugi rodzaj jest wynikiem zastosowania konwersji boksu (§5.1.7) do T, wówczas typ ekspresji warunkowym T.

W przypadku tego wzoru:

val == null ? defVal : "true".equalsIgnoreCase(val) 

Wartość Boolean dla defVal jest auto-unboxed dopasować boolean wynik porównania ciągów. Dzieje się tak pomimo faktu, że wynik trójskładnikowy jest następnie automatycznie zapakowany z powrotem do Boolean - decydując o tym, jak rzutować, trójskładnik nie bierze pod uwagę niczego poza sobą.

2

Ok. Spróbujemy w ten sposób:

public static void main(String[] args){ 
    System.out.println(withTernary(null, null)); 
} 

private static Boolean withTernary(String val, Boolean defVal){ 
    return (val == null ? defVal : Boolean.valueOf("true".equalsIgnoreCase(val))); 
} 

Teraz będzie działać poprawnie. Teraz nie ma tutaj żadnej wersji unboxing i nie spowoduje wyjątku. Ale w inny sposób z powodu nullunbox dostaniesz NPE

public static void main(String[] args){ 
    System.out.println(withTernary(null, null)); //Null Pointer 
} 

private static Boolean withTernary(String val, Boolean defVal){ 
    return (val == null ? defVal : "true".equalsIgnoreCase(val)); 
} 
Powiązane problemy