Dlaczego porównywanie liczby całkowitej z int może rzucać NullPointerException w Javie?
Obserwowanie tej sytuacji było dla mnie bardzo mylące:
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 sądzę, operacja bokserska jest wykonywana jako pierwsza (np. java próbuje wyodrębnić wartość int z null
) i operacja porównawcza ma niższy priorytet, dlatego wyjątek jest wyrzucany.
Pytanie brzmi: dlaczego jest on zaimplementowany w ten sposób w Javie? Dlaczego Boks ma wyższy priorytet niż porównywanie referencji? Albo dlaczego nie zaimplementowali Weryfikacji przed boksem?
W tej chwili wygląda niespójne, gdy NullPointerException
jest rzucane z zawiniętymi prymitywami i nie jest rzucane z typami obiektów true.
6 answers
Krótka Odpowiedź
Kluczowy punkt jest taki:
-
==
Pomiędzy dwoma typami odniesienia jest zawsze porównanie odniesienia- częściej niż Nie, np. z
Integer
iString
, chcesz użyćequals
zamiast
- częściej niż Nie, np. z
-
==
pomiędzy typem odniesienia a typem liczbowym jest zawsze porównanie liczbowe- Typ odniesienia zostanie poddany konwersji unboxingu
- Unboxing
null
zawsze rzucaNullPointerException
Język Java jest językiem, który nie jest językiem ojczystym, lecz językiem ojczystym, który nie jest językiem ojczystym.]}
Powyższe wyrażenia posiadają dla dowolnego poprawny Kod Javy. Przy takim zrozumieniu nie ma żadnej niespójności w przedstawionym fragmencie.
Długa Odpowiedź
Oto odpowiednie sekcje JLS:
JLS 15.21.3 operatory równości odniesienia
==
oraz!=
Jeśli operatory operatora równości są zarówno typu reference, jak i typu null, wtedy operacja jest równouprawnieniem obiektu.
To wyjaśnia, co następuje:
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 {[3] } jest porównaniem równości referencyjnej.
Wyjaśnia to również, co następuje:
System.out.println(new Integer(0) == new Integer(0)); // "false"
System.out.println("X" == "x".toUpperCase()); // "false"
Aby ==
Była równością liczbową, przynajmniej jeden z operandów musi być typem liczbowym :
JLS 15.21.1 operatory równości numerycznej
==
oraz!=
Jeśli operandami operatora równości są oba {[96] } typu numerycznego, lub jeden jest typu numerycznego, a drugi jest zamieniany na typ numeryczny, binarna promocja numeryczna jest wykonywana na operandach. Jeśli promowany Typ operandów to
int
lublong
, to wykonywany jest test równości całkowitej; jeśli promowany Typ tofloat or
double ' ` to test równości zmiennoprzecinkowej to wystąpiliZauważ, że binarna promocja liczbowa wykonuje konwersję zestawu wartości i konwersję rozpakowywania.
To wyjaśnia:
Integer i = null;
if (i == 0) { //NullPointerException
}
Oto fragment Effective Java 2nd Edition, pozycja 49: preferuj prymitywy od prymitywów :
Podsumowując, używaj primitives zamiast boxed primitive, gdy masz wybór. Prymitywne typy są prostsze i szybsze. Jeśli musisz używać pudełkowych prymitywów, uważaj! Autoboxing zmniejsza zwięzłość, ale nie niebezpieczeństwo, używania pudełkowych prymitywów. Kiedy twój program porównuje dwa pudełkowe prymitywy z operatorem
==
, dokonuje porównania tożsamości, co prawie na pewno nie jest tym, czego chcesz. Gdy twój program wykonuje obliczenia typu mieszanego z użyciem prymitywów pudełkowych i nieboskłonowych, rozpakowuje, a gdy twój program rozpakowuje, może rzucićNullPointerException
. Wreszcie, gdy twój program ustawia prymitywne wartości, może to spowodować kosztowne i niepotrzebne obiekty kreacje.
Są miejsca, w których nie masz wyboru, jak tylko używać prymitywów pudełkowych, np. generyków, ale poza tym powinieneś poważnie rozważyć, czy decyzja o użyciu prymitywów pudełkowych jest uzasadniona.
Referencje
-
JLS 4.2. Prymitywne typy i wartości
- " typy liczbowe są typami całkowitymi i zmiennoprzecinkowymi."
-
JLS 5.1.8 Unboxing Conversion
- "Typ A mówi się, że jest zamieniany na typ numeryczny, jeśli jest to typ numeryczny, lub jest to typ odniesienia, który może zostać przekształcony na typ numeryczny przez rozpakowanie konwersji."
- " Unboxing conversion konwertuje [...] od typu
Integer
do typuint
" - "jeśli
r
jestnull
, unboxing conversion rzucaNullPointerException
"
- Podręcznik Języka Java / Autoboxing
- JLS 15.21.1 Numeryczne operatory równości
==
oraz!=
- JLS 15.21.3 operatory równości odniesienia
==
i!=
- JLS 5.6.2 Binary Numeric Promotion
Podobne pytania
- czy podczas porównywania dwóch
Integers
w Javie występuje automatyczne unboxing? - dlaczego to
==
, a nieequals()
? - Java: Jaka jest różnica między autoboxingiem a castingiem?
Podobne pytania
- Jaka jest różnica między int a Integer w Javie / C#?
- czy jest zagwarantowane, że nowa liczba całkowita (i) = = i w Javie? (tak!!! Pudełko jest nieboxed, nie odwrotnie!)
-
dlaczego
int num = Integer.getInteger("123")
rzucaNullPointerException
? (!!!) - Java noob: generyki tylko nad obiektami? (tak, niestety)
- Java
String.equals
versus==
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:25
Twój przykład NPE jest odpowiednikiem tego kodu, dzięki autoboxing :
if ( i.intValue( ) == 0 )
Stąd NPE Jeśli i
jest null
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 12:30:40
if (i == 0) { //NullPointerException
...
}
I jest liczbą całkowitą, a 0 jest liczbą całkowitą, więc w tym, co naprawdę się robi, jest coś takiego
i.intValue() == 0
A to powoduje nullpointer, ponieważ i jest null. Dla String nie mamy tej operacji, dlatego nie ma tam wyjątku.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 13:24:29
Twórcy Javy mogli zdefiniować operator ==
do bezpośredniego działania na operandach różnych typów, w którym to przypadku biorąc pod uwagę Integer I; int i;
Porównanie I==i;
może zadać pytanie "Czy I
zawiera odniesienie do Integer
, którego wartością jest i
?"- pytanie, na które można odpowiedzieć bez trudu, nawet jeśli I
jest null. Niestety, Java nie sprawdza bezpośrednio, czy operandy różnych typów są równe; zamiast tego sprawdza, czy język pozwala typowi któregokolwiek z operandów na być przekonwertowany na typ drugiego i-jeśli tak się stanie-porównuje przekonwertowany operand z niekonwertowanym. Takie zachowanie oznacza, że dla zmiennych x
, y
, i z
z pewnymi kombinacjami typów, możliwe jest posiadanie x==y
i y==z
Ale x!=z
[np. x=16777216f y=16777216 z=16777217]. Oznacza to również, że porównanie I==i
jest tłumaczone jako " Konwertuj I na int
i, jeśli to nie spowoduje wyjątku, porównaj go z i
."
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-26 20:41:06
To dzięki funkcji Javas autoboxing . Kompilator wykrywa, że po prawej stronie porównania używasz prymitywnej liczby całkowitej i musi rozpakować wrapper wartość całkowitą do prymitywnej wartości int, jak również.
Ponieważ nie jest to możliwe (jest null, jak Ustawiłeś) NullPointerException
jest wyrzucane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 12:29:03
W i == 0
Java spróbuje wykonać auto-unboxing i dokonać porównania numerycznego (np. "czy wartość przechowywana w obiekcie wrappera, do którego odnosi się i
, jest taka sama jak wartość 0
?").
Ponieważ i
jest null
unboxing rzuci NullPointerException
.
Rozumowanie wygląda tak:
Pierwsze zdanie JLS § 15.21.1 Numeryczne operatory równości = = i != czyta się tak:
Jeśli operandami operatora równości są oba typu liczbowego, lub jeden jest typu numerycznego, a drugi jest zamieniany (§5.1.8) na typ numeryczny, binarna promocja liczbowa jest wykonywana na operandach (§5.6.2).
Wyraźnie {[1] } jest zamieniana na typ numeryczny, a {[2] } jest typem numerycznym, więc binarna promocja liczbowa jest wykonywana na operandach.
§ 5.6.2 Promocja liczb binarnych mówi (między innymi):
Jeśli którykolwiek z operandów jest typu odniesienia, konwersja unboxingu (§5.1.8) jest wystąpili
§ 5.1.8 Unboxing Conversion mówi (między innymi):
Jeśli r jest null, unboxing conversion rzuca a
NullPointerException
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-28 12:32:16