2015-08-12 8 views
7

ho trovato uno strano comportamento nella versione corrente di Java 8. A mio parere il seguente codice dovrebbe andare bene, ma la JVM lancia una NullPointerException:NullPointerException invece di null (? JVM Bug)

Supplier<Object> s =() -> false ? false : false ? false : null; 
s.get(); // expected: null, actual: NullPointerException 

E ' non importa, che tipo di espressione lambda è (uguale a java.util.function.Function) o quali tipi generici vengono utilizzati. Ci possono anche essere espressioni più significative al posto di false ? :. L'esempio sopra è molto breve. Ecco un esempio più colorata:

Function<String, Boolean> f = s -> s.equals("0") ? false : s.equals("1") ? true : null; 
f.apply("0"); // false 
f.apply("1"); // true 
f.apply("2"); // expected: null, actual: NullPointerException 

Tuttavia questi pezzi di codice funzionano bene:

Supplier<Object> s =() -> null; 
s.get(); // null 

e

Supplier<Object> s =() -> false ? false : null; 
s.get(); // null 

oppure con funzione di:

Function<String, Boolean> f = s -> { 
    if (s.equals("0")) return false; 
    else if (s.equals("1")) return true; 
    else return null; 
}; 
f.apply("0"); // false 
f.apply("1"); // true 
f.apply("2"); // null 

ho provato con due Versioni Java:

~# java -version

versione openjdk "1.8.0_66-interno" OpenJDK Runtime Environment (build 1.8.0_66-b01 interna) OpenJDK a 64 bit di server VM (build 25.66-b01, modalità mista)

C:\>java -version

versione java "1.8.0_51" Java (TM) SE Runtime Environment (costruire 1.8.0_51-b16) Java HotSpot (TM) a 64 bit di server VM (build 25.51-B03, modalità mista)

+4

Com'è diverso da 'Oggetto a = falso? falso: falso? false: null; 'che produce anche un' NullPointerException'? –

+0

Si verifica in Java 7 e Java 8 (OpenJDK). –

+1

@ bayou.io In che modo? Lambda non aggiunge nulla. Il "problema" è espressioni ternarie. –

risposta

7

Questo non ha nulla a che vedere con le espressioni lambda; è semplicemente che il tipo di ritorno dell'operatore ternario in questo caso è boolean, quindi verrà utilizzato l'annullamento automatico.

NPE è gettato anche qui:

public class Main { 

    private static Object method() { 
     return false ? false : false ? false : null; 
    } 

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

Allora, che cosa esattamente sta succedendo qui?

In primo luogo, l'espressione 'incorporato' (false ? false : null) viene valutato secondo JLS §15.25:

L'operatore condizionale è sintatticamente destra-associativo (gruppi IT destra a sinistra). Quindi, a? B: c? D: e? F: g significa lo stesso di a? B: (c? D: (e? F: g)).

Il tipo dell'espressione incorporato è Boolean (inscatolato boolean), in modo che sia false e null può inserirsi in esso.

Poi l'intera espressione è:

false ? false : (Boolean expression) 

Poi, sempre secondo JLS §15.25:

Se una delle seconde e terze operandi è di tipo primitivo T, e il tipo di l'altro è il risultato dell'applicazione della conversione di boxe (§5.1.7) a T, quindi il tipo di espressione condizionale è T.

Così, il primo argomento è di tipo primitivo boolean (T nelle specifiche), il tipo dell'altro è la scatola T (Boolean), quindi il tipo dell'intera espressione è boolean.

Quindi, in runtime, l'espressione incorporata restituisce null, che viene annullato automaticamente a boolean, provocando l'NPE.

+1

beh, il codice OP è in java8, quindi dobbiamo prendere in considerazione il tipo di oggetto target 'Object' e dobbiamo analizzare se dovrebbe influenzare gli operandi; perché unboxing è fatto invece di boxe. – ZhongYu

+0

Allora perché 'falso? falso: falso? Boolean.FALSE: null; gira ancora un NPE. Il secondo operando dell'espressione nidificata è 'Boolean', quindi non verrà applicata alcuna conversione di boxe (§5.1.7). L'espressione esterna sarebbe 'falso? falso: (espressione booleana) '. La riga successiva in JLS §15.25 legge 'Se uno tra il secondo e il terzo operando è del tipo null e il tipo dell'altro è un tipo di riferimento, allora il tipo dell'espressione condizionale è quel tipo di riferimento. OK, ma alla fine sarebbe uguale a 'falso? false: (Boolean) null'. – steffen

+1

'falso? falso: falso? Boolean.FALSE: null' è la stessa cosa. L'espressione ternaria incorporata restituisce 'Boolean', e l'intera espressione restituisce' boolean', perché è il tipo primitivo 'T' citato sopra. –