Obserwowanie tej sytuacji było dla mnie bardzo zagmatwane:
Integer i = null;
String str = null;
if (i == null) { //Nothing happens
...
}
if (str == null) { //Nothing happens
}
if (i == 0) { //NullPointerException
...
}
if (str == "0") { //Nothing happens
...
}
Tak więc, jak myślę, operacja pakowania jest wykonywana jako pierwsza (tj. Java próbuje wyodrębnić wartość int null
), a operacja porównania ma niższy priorytet, dlatego zgłaszany jest wyjątek.
Pytanie brzmi: dlaczego jest tak zaimplementowany w Javie? Dlaczego boks ma wyższy priorytet niż porównywanie odniesień? Albo dlaczego nie wdrożyli weryfikacji null
przed boksowaniem?
W tej chwili wygląda to niespójnie, gdy NullPointerException
jest wyrzucane z opakowanymi prymitywami i nie jest wyrzucane z prawdziwymi typami obiektów.
java
nullpointerexception
boxing
rzymski
źródło
źródło
Odpowiedzi:
Krótka odpowiedź
Kluczowa kwestia jest taka:
==
między dwoma typami odwołań jest zawsze porównaniem referencyjnymInteger
iString
,equals
zamiast tego chciałbyś użyć==
między typem referencyjnym a liczbowym typem pierwotnym jest zawsze porównanie liczbowenull
zawsze rzucaNullPointerException
String
, w rzeczywistości NIE jest to typ prymitywnyPowyższe instrukcje dotyczą dowolnego poprawnego kodu Java. Z tym zrozumieniem nie ma żadnych niespójności w przedstawionym fragmencie.
Długa odpowiedź
Oto odpowiednie sekcje JLS:
To wyjaśnia następujące kwestie:
Integer i = null; String str = null; if (i == null) { // Nothing happens } if (str == null) { // Nothing happens } if (str == "0") { // Nothing happens }
Oba operandy są typami referencyjnymi i dlatego
==
jest porównaniem równości odwołań.To wyjaśnia również następujące kwestie:
System.out.println(new Integer(0) == new Integer(0)); // "false" System.out.println("X" == "x".toUpperCase()); // "false"
Aby
==
być równością liczbową, co najmniej jeden z argumentów musi być typu liczbowego :To wyjaśnia:
Integer i = null; if (i == 0) { //NullPointerException }
Oto fragment z Effective Java 2nd Edition, pozycja 49: Preferuj prymitywy od pudełkowych :
Są miejsca, w których nie masz innego wyboru, jak tylko użyć prymitywów pudełkowych, np. Generycznych, ale w przeciwnym razie powinieneś poważnie rozważyć, czy decyzja o użyciu prymitywów pudełkowych jest uzasadniona.
Bibliografia
Integer
na typint
”r
taknull
, po rozpakowaniu konwersji generujeNullPointerException
”==
i!=
==
i!=
Powiązane pytania
Integers
w Javie występuje automatyczne rozpakowywanie?==
ale nieequals()
?Powiązane pytania
int num = Integer.getInteger("123")
rzucaNullPointerException
? (!!!)String.equals
a==
źródło
someRef == 0
zawsze jest to porównanie numeryczne, jest to bardzo rozsądny wybór, ponieważ porównywanie odwołań do dwóch prymitywów pudełkowych jest prawie zawsze błędem programisty. Domyślne porównywanie odniesień w tym przypadku byłoby bezcelowe.(myInteger == 0)
z(myInteger != null && myInteger == 0)
zamiast opierania się na autora do napisania tej szablonowe kod null sprawdzania? IMO Powinienem być w stanie sprawdzić,if (myBoolean)
a to powinno ocenićtrue
wtedy i tylko wtedy, gdy podstawowa wartość jest konkretnatrue
- nie powinienem najpierw musieć sprawdzać wartości zerowej.Twój przykład NPE jest odpowiednikiem tego kodu dzięki autoboxingowi :
if ( i.intValue( ) == 0 )
Stąd NPE, jeśli
i
jestnull
.źródło
if (i == 0) { //NullPointerException ... }
i jest liczbą całkowitą, a 0 jest liczbą całkowitą, więc w tym, co naprawdę się robi, wygląda to tak
i.intValue() == 0
A to powoduje nullPointer, ponieważ i jest null. Dla String nie mamy tej operacji, dlatego nie ma tam wyjątku.
źródło
Twórcy Javy mogli zdefiniować
==
operator tak, aby działał bezpośrednio na operandach różnych typów. W takim przypadku, biorącInteger I; int i;
pod uwagę porównanie,I==i;
można zadać pytanie „CzyI
zawiera odniesienie do elementu,Integer
którego wartość jesti
?” - pytanie, na które można odpowiedzieć bez trudności nawet jeśliI
jest zerowa. Niestety, Java nie sprawdza bezpośrednio, czy operandy różnych typów są równe; zamiast tego sprawdza, czy język zezwala na konwersję jednego z typów operandów na typ drugiego i - jeśli tak - porównuje przekonwertowany operand z nieprzekonwertowanym. Takie zachowanie oznacza, że dla zmiennychx
,y
orazz
z niektóre kombinacje typów, to jest możliwe, żex==y
iy==z
alex!=z
[np. x = 16777216f y = 16777216 z = 16777217]. Oznacza to również, że porównanieI==i
jest tłumaczone jako „Konwertuj I na an,int
a jeśli to nie zgłasza wyjątku, porównaj je zi
”.źródło
==
w taki sposób, że jeśli we wszystkich przypadkachx==y
,y==z
ix==z
wszystkich kompilacji bez zarzutu, trzy porównania będzie zachowywać się jak relacją równoważności. Ciekawe, że projektanci naciskają na różne wymyślne funkcje językowe, ale ignorują aksjomatyczną zgodność.Dzieje się tak z powodu funkcji automatycznego blokowania Javas . Kompilator wykrywa, że po prawej stronie porównania używasz prymitywnej liczby całkowitej i musi również rozpakować opakowującą wartość Integer na pierwotną wartość int.
Ponieważ nie jest to możliwe (jest zerowe, jak wskazałeś),
NullPointerException
zostaje wyrzucony.źródło
W
i == 0
Javie spróbuje wykonać automatyczne rozpakowywanie i wykonać porównanie liczbowe (tj. "Czy wartość przechowywana w obiekcie opakowującym odwołuje się doi
tej samej wartości0
?").Ponieważ
i
jestnull
to rozpakowanie, wyrzuci plikNullPointerException
.Rozumowanie wygląda następująco:
Pierwsze zdanie JLS § 15.21.1 Numerical Equality Operators == i! = Brzmi następująco:
Oczywiście
i
można konwertować na typ liczbowy i0
jest typem liczbowym, więc binarna promocja liczbowa jest wykonywana na operandach.§ 5.6.2 Binarna promocja numeryczna mówi (między innymi):
§ 5.1.8 Unboxing Conversion mówi (między innymi):
źródło
Po prostu napisz metodę i wywołaj ją, aby uniknąć wyjątku NullPointerException.
public static Integer getNotNullIntValue(Integer value) { if(value!=null) { return value; } return 0; }
źródło