Boolean.valueOf () produce a volte NullPointerException


115

Ho questo codice:

package tests;

import java.util.Hashtable;

public class Tests {

    public static void main(String[] args) {

        Hashtable<String, Boolean> modifiedItems = new Hashtable<String, Boolean>();

        System.out.println("TEST 1");
        System.out.println(modifiedItems.get("item1")); // Prints null
        System.out.println("TEST 2");
        System.out.println(modifiedItems.get("item1") == null); // Prints true
        System.out.println("TEST 3");
        System.out.println(Boolean.valueOf(null)); // Prints false
        System.out.println("TEST 4");
        System.out.println(Boolean.valueOf(modifiedItems.get("item1"))); // Produces NullPointerException
        System.out.println("FINISHED!"); // Never executed
    }
}

Il mio problema è che non capisco perché il Test 3 funziona bene (stampa falsee non produce NullPointerException) mentre il Test 4 lancia un file NullPointerException. Come puoi vedere nei test 1 e 2 , nulle modifiedItems.get("item1")sono uguali e null.

Il comportamento è lo stesso in Java 7 e 8.


modifiedItems.get ("item1") questo è nullo, ne sei consapevole, ma presumi che il passaggio di questo a valueOf non finirà in un NPE?
Stultuske

16
@Stultuske: è una domanda valida, dato che solo due righe sopra il passaggio di un letterale nullalla stessa funzione non genera un NPE! C'è una buona ragione, ma a prima vista è certamente
fonte di

25
Sono impressionato. Questa è la domanda sull'eccezione del puntatore nullo più interessante che ho visto da anni.
candied_orange

@ Jeroen questo non è un dupe di quella domanda . Anche se è vero che l'unboxing è comune ai due problemi, qui non c'è confronto. La cosa fondamentale di questa domanda è che si verifica a causa del modo in cui vengono risolti i sovraccarichi; e questa è una cosa abbastanza diversa da come ==viene applicata.
Andy Turner

Risposte:


178

Devi guardare attentamente quale sovraccarico viene invocato:

  • Boolean.valueOf(null) sta invocando Boolean.valueOf(String) . Questo non genera un valore NPEpari se fornito con un parametro null.
  • Boolean.valueOf(modifiedItems.get("item1"))sta invocando Boolean.valueOf(boolean), perché modifiedItemsi valori di sono di tipo Boolean, il che richiede una conversione unboxing. Poiché modifiedItems.get("item1")è null, è l'unboxing di quel valore - non il Boolean.valueOf(...)- che genera l'NPE.

Le regole per determinare quale sovraccarico viene invocato sono piuttosto complicate , ma più o meno vanno così:

  • In un primo passaggio, viene cercata una corrispondenza di metodo senza consentire il boxing / unboxing (né i metodi di arity variabile).

    • Poiché nullè un valore accettabile per a Stringma non boolean, Boolean.valueOf(null)è abbinato a Boolean.valueOf(String)in questo passaggio;
    • Booleannon è accettabile per Boolean.valueOf(String)o Boolean.valueOf(boolean), quindi nessun metodo è abbinato in questo passaggio per Boolean.valueOf(modifiedItems.get("item1")).
  • In un secondo passaggio, viene cercata una corrispondenza del metodo, consentendo il boxing / unboxing (ma ancora non metodi di arity variabili).

    • A Booleanpuò essere unboxed boolean, quindi Boolean.valueOf(boolean)è abbinato Boolean.valueOf(modifiedItems.get("item1"))in questo passaggio; ma una conversione unboxing deve essere inserita dal compilatore per invocarla:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
  • (C'è un terzo passaggio che consente metodi variabili di arità, ma non è rilevante qui, poiché i primi due passaggi corrispondono a questi casi)


3
Il codice potrebbe essere più chiaro se usassimo Boolean.valueOf(modifiedItems.get("item1").booleanValue())nel codice sorgente invece di Boolean.valueOf(modifiedItems.get("item1"))?
Causing UnderflowsEverywhere

1
@CausingUnderflowsEverywhere non proprio - è davvero difficile vederlo .booleanValue()sepolto nell'espressione. Due osservazioni: 1) l'auto (un) boxing è una caratteristica deliberata di Java per rimuovere il cruft sintattico; farlo da soli è possibile, ma non idiomatico; 2) questo non ti aiuta affatto - certamente non impedisce che si verifichi il problema, né fornisce alcuna informazione extra quando si verifica l'errore (la traccia dello stack sarebbe identica, perché il codice eseguito è identico).
Andy Turner

@CausingUnderflowsEverywhere è meglio utilizzare gli strumenti per evidenziare i problemi, ad esempio intellij ti farebbe guadagnare sul potenziale NPE qui.
Andy Turner

13

Poiché modifiedItems.getrestituisce a Boolean(che non è calcinabile in a String), la firma che verrebbe utilizzata è Boolean.valueOf(boolean), dove Booleanviene inviato in uscita a una primitiva boolean. Una volta che nullviene restituito lì, la posta in uscita fallisce con un file NullPointerException.


11

Firma del metodo

Il metodo Boolean.valueOf(...)ha due firme:

  1. public static Boolean valueOf(boolean b)
  2. public static Boolean valueOf(String s)

Il tuo modifiedItemsvalore è Boolean. Non puoi trasmettere BooleanaString conseguenza verrà scelta la prima firma

Unboxing booleano

Nella tua dichiarazione

Boolean.valueOf(modifiedItems.get("item1"))

che può essere letto come

Boolean.valueOf(modifiedItems.get("item1").booleanValue())   

Tuttavia, modifiedItems.get("item1")ritorni nullquindi in pratica avrai

null.booleanValue()

che ovviamente porta ad a NullPointerException


Formulazione errata, grazie per il puntamento e la risposta viene aggiornata in seguito al tuo feedback. Mi scuso, non ho visto la tua risposta mentre scrivevo e vedo che la mia assomiglia alla tua. Devo rimuovere la mia risposta per evitare confusione per OP?
Al-un

4
Non cancellarlo sul mio account. Ricorda, questo non è un gioco a somma zero: le persone possono (e fanno) votare più risposte.
Andy Turner

3

Come Andy ha già descritto molto bene il motivo di NullPointerException:

che è dovuto all'annullamento booleano:

Boolean.valueOf(modifiedItems.get("item1"))

convertiti in:

Boolean.valueOf(modifiedItems.get("item1").booleanValue())

in fase di esecuzione e quindi lancia NullPointerExceptionif modifiedItems.get("item1")è null.

Ora vorrei aggiungere un altro punto qui che un-boxing delle seguenti classi alle rispettive primitive può anche produrre NullPointerExceptionun'eccezione se i loro corrispondenti oggetti restituiti sono nulli.

  1. byte - Byte
  2. char - Carattere
  3. float - Float
  4. int - Integer
  5. lungo lungo
  6. breve - Breve
  7. doppia - Doppia

Ecco il codice:

    Hashtable<String, Boolean> modifiedItems1 = new Hashtable<String, Boolean>();
    System.out.println(Boolean.valueOf(modifiedItems1.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Byte> modifiedItems2 = new Hashtable<String, Byte>();
    System.out.println(Byte.valueOf(modifiedItems2.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Character> modifiedItems3 = new Hashtable<String, Character>();
    System.out.println(Character.valueOf(modifiedItems3.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Float> modifiedItems4 = new Hashtable<String, Float>();
    System.out.println(Float.valueOf(modifiedItems4.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Integer> modifiedItems5 = new Hashtable<String, Integer>();
    System.out.println(Integer.valueOf(modifiedItems5.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Long> modifiedItems6 = new Hashtable<String, Long>();
    System.out.println(Long.valueOf(modifiedItems6.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Short> modifiedItems7 = new Hashtable<String, Short>();
    System.out.println(Short.valueOf(modifiedItems7.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Double> modifiedItems8 = new Hashtable<String, Double>();
    System.out.println(Double.valueOf(modifiedItems8.get("item1")));//Exception in thread "main" java.lang.NullPointerException

1
"Convertito in ... in fase di esecuzione" viene convertito in quello in fase di compilazione.
Andy Turner

0

Un modo per capirlo è quando Boolean.valueOf(null) viene invocato, viene precisamente detto a java di valutare null.

Tuttavia, quando Boolean.valueOf(modifiedItems.get("item1")) viene invocato, a java viene chiesto di ottenere un valore dall'HashTable di tipo di oggetto Boolean, ma non trova il tipo Boolean, trova invece un vicolo cieco (null) anche se si aspettava Boolean. L'eccezione NullPointerException viene generata perché i creatori di questa parte di java hanno deciso che questa situazione è un'istanza di qualcosa nel programma che va storto che richiede l'attenzione del programmatore. (È successo qualcosa di non intenzionale.)

In questo caso è più la differenza tra dichiarare deliberatamente che si intendeva che il null fosse lì e java trovare un riferimento mancante a un oggetto (null) in cui si intendeva trovare un oggetto.

Ulteriori informazioni su NullPointerException in questa risposta: https://stackoverflow.com/a/25721181/4425643


Se qualcuno può aiutare a migliorare questa risposta, stavo pensando a una parola che si riferisce al programmatore che scrive qualcosa con chiara intenzione, senza ambiguità
CausingUnderflowsEverywhere
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.