Mam ten kod:
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
}
}
Mój problem polega na tym, że nie rozumiem, dlaczego Test 3 działa dobrze (drukuje false
i nie produkuje NullPointerException
), tymczasem Test 4 wyrzuca plik NullPointerException
. Jak widać w testach 1 i 2 , null
i modifiedItems.get("item1")
są równe i null
.
Zachowanie jest takie samo w Javie 7 i 8.
java
nullpointerexception
boolean
David E.
źródło
źródło
null
do tej samej funkcji nie generują NPE! Jest ku temu dobry powód, ale na pierwszy rzut oka jest to z pewnością mylące :-)==
stosowania.Odpowiedzi:
Musisz uważnie przyjrzeć się, które przeciążenie jest wywoływane:
Boolean.valueOf(null)
wołaBoolean.valueOf(String)
. To nie zgłaszaNPE
nawet, jeśli jest dostarczony z parametrem null.Boolean.valueOf(modifiedItems.get("item1"))
wywołujeBoolean.valueOf(boolean)
, ponieważmodifiedItems
wartości są typuBoolean
, który wymaga konwersji po rozpakowaniu. PonieważmodifiedItems.get("item1")
jestnull
, to rozpakowanie tej wartości - a nieBoolean.valueOf(...)
- powoduje odrzucenie NPE.Zasady określania, które przeciążenie jest wywoływane, są dość zawiłe , ale z grubsza wyglądają tak:
W pierwszym przebiegu szukane jest dopasowanie metody bez zezwalania na pakowanie / rozpakowywanie (ani metody ze zmienną arancją).
null
jest to dopuszczalna wartość dla a,String
ale nieboolean
,Boolean.valueOf(null)
jest dopasowywana doBoolean.valueOf(String)
w tym przebiegu;Boolean
nie jest akceptowalne dla alboBoolean.valueOf(String)
lubBoolean.valueOf(boolean)
, więc żadna metoda nie jest dopasowywana w tym przebiegu forBoolean.valueOf(modifiedItems.get("item1"))
.W drugim przebiegu wyszukiwane jest dopasowanie metody, co pozwala na umieszczanie w pudełku / rozpakowywanie (ale nadal nie metody ze zmienną aranżacją).
Boolean
można rozpakować doboolean
, więcBoolean.valueOf(boolean)
jest dopasowywaneBoolean.valueOf(modifiedItems.get("item1"))
w tym przebiegu; ale kompilator musi wstawić konwersję po rozpakowaniu, aby ją wywołać:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
(Istnieje trzeci przebieg zezwalający na metody ze zmienną aranżacją, ale nie ma to znaczenia w tym przypadku, ponieważ pierwsze dwa przebiegi pasowały do tych przypadków)
źródło
Boolean.valueOf(modifiedItems.get("item1").booleanValue())
w kodzie źródłowym zamiastBoolean.valueOf(modifiedItems.get("item1"))
?.booleanValue()
w wyrażeniu. Dwie spostrzeżenia: 1) automatyczne (od) boksowanie to celowa funkcja Javy służąca do usuwania składniowego cruft; robienie tego samemu jest możliwe, ale nie idiomatyczne; 2) to w ogóle Ci nie pomaga - z pewnością nie zapobiega występowaniu problemu ani nie daje żadnych dodatkowych informacji, gdy wystąpi awaria (ślad stosu byłby identyczny, ponieważ wykonywany kod jest identyczny).Ponieważ
modifiedItems.get
zwraca aBoolean
(którego nie można rzutować na aString
), użyty zostałby podpisBoolean.valueOf(boolean)
, w którymBoolean
jest wysyłany do prymitywuboolean
. Ponull
powrocie tam wysyłanie kończy się niepowodzeniem z rozszerzeniemNullPointerException
.źródło
Podpis metody
Metoda
Boolean.valueOf(...)
ma dwa podpisy:public static Boolean valueOf(boolean b)
public static Boolean valueOf(String s)
Twoja
modifiedItems
wartość toBoolean
. Nie możesz przesyłaćBoolean
do,String
więc w konsekwencji zostanie wybrany pierwszy podpisBoolean unboxing
W swoim oświadczeniu
które można odczytać jako
Jednak
modifiedItems.get("item1")
wraca,null
więc w zasadzie będziesz miećco oczywiście prowadzi do
NullPointerException
źródło
Jak Andy bardzo dobrze opisał powód
NullPointerException
:co wynika z logicznego un-boxingu:
przekształcić się w:
w czasie wykonywania, a następnie zgłasza
NullPointerException
ifmodifiedItems.get("item1")
jest null.Teraz chciałbym dodać jeszcze jeden punkt, że rozpakowywanie następujących klas do ich odpowiednich prymitywów może również powodować
NullPointerException
wyjątek, jeśli odpowiadające im zwracane obiekty mają wartość null.Oto kod:
źródło
Sposób na zrozumienie tego jest taki, że kiedy
Boolean.valueOf(null)
jest wywoływany, java jest precyzyjnie instruowana, aby oszacować wartość null.Jednak po
Boolean.valueOf(modifiedItems.get("item1"))
wywołaniu java otrzymuje polecenie uzyskania wartości z HashTable typu obiektu Boolean, ale nie znajduje typu Boolean, zamiast tego znajduje ślepy zaułek (null), mimo że oczekiwał wartości Boolean. Wyjątek NullPointerException jest generowany, ponieważ twórcy tej części javy zdecydowali, że ta sytuacja jest przypadkiem nieprawidłowego działania programu, który wymaga uwagi programisty. (Stało się coś niezamierzonego.)W tym przypadku bardziej chodzi o różnicę między celowym deklarowaniem, że zamierzasz znaleźć wartość null, a java znajdowaniem brakującego odniesienia do obiektu (null), w którym obiekt miał zostać znaleziony.
Zobacz więcej informacji o wyjątku NullPointerException w tej odpowiedzi: https://stackoverflow.com/a/25721181/4425643
źródło