Boolean.valueOf () erzeugt manchmal eine NullPointerException

115

Ich habe diesen Code:

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
    }
}

Mein Problem ist, dass ich nicht verstehe, warum Test 3 gut funktioniert (es druckt falseund produziert nicht NullPointerException), während Test 4 a wirft NullPointerException. Wie Sie in den Tests 1 und 2 sehen können , sind nullund modifiedItems.get("item1")gleich und null.

Das Verhalten ist in Java 7 und 8 dasselbe.

David E.
quelle
modifiedItems.get ("item1") dies ist null, Sie sind sich dessen bewusst, aber Sie gehen davon aus, dass die Übergabe an einen valueOf nicht in einer NPE endet?
Stultuske
16
@Stultuske: Dies ist eine gültige Frage, da nur zwei Zeilen über der Übergabe eines Literals nullan dieselbe Funktion keine NPE erzeugen! Es gibt einen guten Grund dafür, aber es ist auf den ersten Blick sicherlich verwirrend :-)
psmears
25
Ich bin beeindruckt. Dies ist die interessanteste Nullzeiger-Ausnahmefrage, die ich seit Jahren gesehen habe.
candied_orange
@Jeroen das ist kein Betrüger dieser Frage . Zwar ist das Unboxing den beiden Problemen gemeinsam, doch findet hier kein Vergleich statt. Das Wichtigste an dieser Frage ist, dass sie aufgrund der Art und Weise auftritt, wie Überlastungen gelöst werden. und das ist etwas ganz anderes ==als die Anwendung.
Andy Turner

Antworten:

178

Sie müssen genau prüfen, welche Überlastung ausgelöst wird:

  • Boolean.valueOf(null)ruft auf Boolean.valueOf(String). Das wirft keinenNPE Even aus, wenn es mit einem Null-Parameter geliefert wird.
  • Boolean.valueOf(modifiedItems.get("item1"))wird aufgerufen Boolean.valueOf(boolean), da modifiedItemsdie Werte vom Typ sind Boolean, was eine Unboxing-Konvertierung erfordert. Seitdem modifiedItems.get("item1")ist nulles das Unboxing dieses Wertes - nicht das Boolean.valueOf(...)-, das die NPE auslöst.

Die Regeln zum Bestimmen, welche Überladung aufgerufen wird, sind ziemlich haarig , aber sie sehen ungefähr so ​​aus:

  • In einem ersten Durchgang wird nach einer Methodenübereinstimmung gesucht, ohne dass Boxing / Unboxing (oder Methoden mit variabler Arität) zulässig sind.

    • Da nullein akzeptabler Wert für a Stringaber nicht ist boolean, Boolean.valueOf(null)wird Boolean.valueOf(String)in diesem Durchgang abgeglichen ;
    • Booleanist nicht akzeptabel , entweder Boolean.valueOf(String)oder Boolean.valueOf(boolean), so wird kein Verfahren für die in diesem Durchlauf abgestimmt Boolean.valueOf(modifiedItems.get("item1")).
  • In einem zweiten Durchgang wird nach einer Methodenübereinstimmung gesucht, die das Boxen / Entpacken ermöglicht (jedoch immer noch keine Methoden mit variabler Arität).

    • A Booleankann entpackt werden, booleanwird also in diesem Durchgang Boolean.valueOf(boolean)abgeglichen Boolean.valueOf(modifiedItems.get("item1")). Der Compiler muss jedoch eine Unboxing-Konvertierung einfügen, um sie aufzurufen:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
  • (Es gibt einen dritten Durchgang, der Methoden mit variabler Arität zulässt, aber das ist hier nicht relevant, da die ersten beiden Durchgänge diesen Fällen entsprachen.)

Andy Turner
quelle
3
Könnte Code klarer sein, wenn wir Boolean.valueOf(modifiedItems.get("item1").booleanValue())im Quellcode anstelle von verwenden Boolean.valueOf(modifiedItems.get("item1"))?
CausingUnderflowsEverywhere
1
@CausingUnderflowsEverywhere nicht wirklich - es ist wirklich schwer zu sehen, dass .booleanValue()im Ausdruck begraben. Zwei Beobachtungen: 1) Auto (Un) Boxing ist eine bewusste Funktion von Java, um syntaktische Cruft zu entfernen. es selbst zu tun ist möglich, aber nicht idiomatisch; 2) Dies hilft Ihnen überhaupt nicht - es verhindert sicherlich nicht das Auftreten des Problems und gibt auch keine zusätzlichen Informationen, wenn der Fehler auftritt (die Stapelverfolgung wäre identisch, da der ausgeführte Code identisch ist).
Andy Turner
@CausingUnderflowsEverywhere ist es besser, Tools zu verwenden, um die Probleme hervorzuheben, z. B. würde Intellij Sie hier über potenzielle NPE informieren.
Andy Turner
13

Da modifiedItems.geta zurückgegeben wird Boolean(was nicht in a umgewandelt werden kann String), wird die Signatur verwendet Boolean.valueOf(boolean), bei der Booleandas an ein Grundelement gesendet wird boolean. Sobald nulles dort zurückgegeben wird, schlägt der Postausgang mit a fehl NullPointerException.

Mureinik
quelle
11

Methodensignatur

Die Methode Boolean.valueOf(...)hat zwei Signaturen:

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

Ihr modifiedItemsWert ist Boolean. Sie können nicht werfen BooleanzuString daher wird die erste Signatur ausgewählt

Boolesches Unboxing

In deiner Aussage

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

was gelesen werden kann als

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

Gibt jedoch modifiedItems.get("item1")zurück, nullso dass Sie im Grunde haben

null.booleanValue()

was offensichtlich zu einem führt NullPointerException

Al-un
quelle
Falscher Wortlaut, danke für das Zeigen und die Antwort wird nach Ihrem Feedback aktualisiert. Entschuldigung, ich habe Ihre Antwort beim Schreiben nicht gesehen und ich sehe, dass meine wie Ihre aussieht. Sollte ich meine Antwort entfernen, um Verwirrung bei OP zu vermeiden?
Al-un
4
Löschen Sie es nicht in meinem Konto. Denken Sie daran, dies ist kein Nullsummenspiel: Die Leute können (und tun) mehrere Antworten positiv bewerten.
Andy Turner
3

Wie Andy schon sehr gut beschrieben hat der Grund von NullPointerException:

was auf boolesches Unboxing zurückzuführen ist:

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

konvertiert werden in:

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

zur Laufzeit und dann wird geworfen, NullPointerExceptionwenn modifiedItems.get("item1")null ist.

Jetzt möchte ich noch einen Punkt hinzufügen, dass das Aufheben des Boxens der folgenden Klassen zu ihren jeweiligen Grundelementen auch eine NullPointerExceptionAusnahme erzeugen kann, wenn ihre entsprechenden zurückgegebenen Objekte null sind.

  1. Byte - Byte
  2. char - Charakter
  3. float - Float
  4. int - Integer
  5. lang Lang
  6. kurz - kurz
  7. double - Double

Hier ist der Code:

    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
Mohit Tyagi
quelle
1
"Zur Laufzeit in ... konvertiert" wird zur Kompilierungszeit in dieses konvertiert.
Andy Turner
0

Eine Möglichkeit, dies zu verstehen, besteht Boolean.valueOf(null)darin, dass Java beim Aufrufen genau angewiesen wird, null auszuwerten.

Beim Boolean.valueOf(modifiedItems.get("item1"))Aufrufen wird Java jedoch angewiesen, einen Wert aus der HashTable des Objekttyps Boolean abzurufen, findet jedoch nicht den Typ Boolean, sondern eine Sackgasse (null), obwohl Boolean erwartet wird. Die NullPointerException-Ausnahme wird ausgelöst, weil die Ersteller dieses Teils von Java entschieden haben, dass diese Situation eine Instanz eines Fehlers im Programm ist, der die Aufmerksamkeit des Programmierers erfordert. (Etwas Unbeabsichtigtes ist passiert.)

In diesem Fall ist es eher der Unterschied zwischen der absichtlichen Erklärung, dass Sie beabsichtigt haben, dass die Null vorhanden sein soll, und der Suche nach einem fehlenden Verweis auf ein Objekt (null), bei dem ein Objekt gefunden werden soll.

Weitere Informationen zu NullPointerException finden Sie in dieser Antwort: https://stackoverflow.com/a/25721181/4425643

Verursachen von Unterflüssen überall
quelle
Wenn jemand helfen kann, diese Antwort zu verbessern, dachte ich an ein Wort, das sich darauf bezieht, dass der Programmierer etwas mit klarer Absicht und ohne Mehrdeutigkeit
schreibt