Java operator warunkowy: Typ wyniku

Jestem trochę zdziwiony operatorem warunkowym. Rozważmy następujące dwie linie:

Float f1 = false? 1.0f: null;
Float f2 = false? 1.0f: false? 1.0f: null;

Dlaczego f1 staje się null, a drugie stwierdzenie rzuca NullPointerException?

Langspec-3.0 para 15.25 sais:

W przeciwnym razie drugi i trzeci operand są odpowiednio typu S1 i S2. Niech T1 będzie typem wynikającym z zastosowania konwersji boksu na S1, oraz niech T2 jest typem wynikającym z zastosowania konwersji boksu na S2. Rodzaj na wyrażenie warunkowe jest wynikiem zastosowania konwersji przechwytywania (§5.1.10) do lub (T1, T2) (§15.12.2.7).

Więc dla false?1.0f:null T1 jest zmiennoprzecinkowym, a T2 jest typem null. Ale jaki jest rezultat lub(T1,T2)? Ten punkt 15.12.2.7 jest po prostu trochę za dużo ...

BTW, używam 1.6.0_18 na Windows.

PS: wiem, że Float f2 = false? (Float) 1.0f: false? (Float) 1.0f: null; nie rzuca NPE.

Author: Mechanical snail, 2010-04-11

5 answers

Różnica polega na statycznym wpisywaniu wyrażeń w czasie kompilacji:

Podsumowanie

E1: `(false ? 1.0f : null)`
    - arg 2 '1.0f'           : type float,
    - arg 3 'null'           : type null 
    - therefore operator ?:  : type Float (see explanation below)
    - therefore autobox arg2
    - therefore autobox arg3

E2: `(false ? 1.0f : (false ? 1.0f : null))`
    - arg 2 '1.0f'                    : type float
    - arg 3 '(false ? 1.0f : null)'   : type Float (this expr is same as E1)
    - therefore, outer operator ?:    : type float (see explanation below)
    - therefore un-autobox arg3

Szczegółowe Wyjaśnienie:

Oto moje zrozumienie z czytania przez spec i pracy wstecz od wyniku masz. Sprowadza się to do typu trzeciego operandu f2 inner conditional jest typem null, podczas gdy typ trzeciego operandu F2 outer conditional jest uważany za Float.

Uwaga: Ważne jest, aby pamiętać, że określenie typu i wstawianie kodu Boks / unboxing odbywa się w czasie kompilacji. Rzeczywiste wykonanie kodu boxing / unboxing odbywa się w czasie wykonywania.

Float f1 = (false ? 1.0f : null);
Float f2 = (false ? 1.0f : (false ? 1.0f : null));

F1 warunkowe i F2 wewnętrzne warunkowe: (false ? 1.0 f: null)

F1 warunkowe i F2 wewnętrzne warunkowe są identyczne: (false ? 1.0 f: null) . Typy operand w F1 conditional i F2 Inner conditional są:

type of second operand = float
type of third operand = null type (§4.1)

Większość zasad w §15.25 są pomijane i ta końcowa ocena jest rzeczywiście stosowana:

W przeciwnym razie 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 = float
S2 = null type
T1 = Float
T2 = null type
type of the f1 and f2 inner conditional expressions = Float

Ponieważ dla f1 przypisanie jest do zmiennej referencyjnej Float, wynik wyrażenia (null) jest pomyślnie przypisany.

For F2 outer conditional: (false ? 1.0 f: [F2 wewnętrzny warunkowy])

Dla warunku zewnętrznego F2, typy są następujące:

type of second operand = float
type of third operand = Float

Zwróć uwagę na różnicę typów operandów w porównaniu z wewnętrznymi warunkami F1/f2, które odwołują się bezpośrednio do null (§4.1). Z powodu tej różnicy posiadania 2 typów numerycznych, reguła ta z §15.12.2.7 dotyczy:

  • W przeciwnym razie, jeśli drugi i trzeci operand mają typy, które są konwertowalne (§5.1.8) do typów liczbowych jest wtedy kilka przypadków: ...

    • inaczej, binarna promocja liczbowa (§5.6.2) jest stosowany do typów operand, a typ wyrażenia warunkowego jest typem promowanym drugi i trzeci operand. Zauważ, że binary numeric promotion wykonuje konwersję unboxing (§5.1.8) i konwersja zestawu wartości(§5.1.13).

Z powodu konwersji unboxingu wykonanej na wyniku wewnętrznego warunku F2 (null), wywoływany jest wyjątek NullPointerException.

 26
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
2011-09-20 22:10:47

Poniższy tekst rzuci NPE, gdy spróbujesz przypisać null do prymitywnego

    float f1 = false ? 1.0f: null;

Że wierzę, że to co powoduje NPE w drugim stwierdzeniu. Ponieważ pierwszy trójnóg zwraca float dla true, próbuje również przekonwertować false na float.

Pierwsze polecenie nie przekonwertuje NA null, ponieważ wymaganym wynikiem jest Float

To na przykład nie rzuci NPE, ponieważ jego nie musi już konwertować do prymitywnego

    Float f = false? new Float(1.0f): true ? null : 1.0f;
 2
Author: objects,
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-04-11 00:28:27

Myślę, że przepisanie kodu sprawia, że wyjaśnienie jest jaśniejsze:

    float f = 1.0f;

    Float null_Float  = false?        f  : null;       // float + null  -> OK
    Float null_Float2 = false? (Float)f  : null_Float; // Float + Float -> OK
    Float npe         = false?        f  : null_Float; // float + Float -> NPE

Tak więc NPE jest wtedy, gdy próbujemy zrobić coś takiego:

Float npe = false? 1.0f : (Float)null;
 2
Author: JRL,
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-04-11 01:26:31

Być albo nie być, oto jest pytanie. :)

Edit: właściwie, patrząc bliżej wydaje się, że ten przypadek jest w rzeczywistości mieszanką Hamlet (operator trójkowy i typy całkowe zawinięte) iElvis (auto-unboxing null) puzzlers. W każdym razie mogę tylko polecić obejrzenie filmu, jest to bardzo edukacyjne i przyjemne.

 2
Author: kloffy,
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-04-11 01:48:38

Wygląda na to, że JVM próbuje rozpakować drugą wartość null do float zamiast Float, stąd NullPointerException. Sam raz Uderzyłem. Moim zdaniem druga if robi to, ponieważ true część pierwszej if ocenia jako zmiennoprzecinkowy, a nie zmiennoprzecinkowy.

Po zastanowieniu się nad tym, myślę, że to sposób Java mówi ci, że robisz coś dziwnego. Tylko nie gnieżdżcie się w trójkę i będzie dobrze: -)

 1
Author: Slava Imeshev,
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-04-11 02:22:16