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.

Author: xan, 2010-07-28

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 i String, chcesz użyć equals zamiast
  • == pomiędzy typem odniesienia a typem liczbowym jest zawsze porównanie liczbowe
    • Typ odniesienia zostanie poddany konwersji unboxingu
    • Unboxing null zawsze rzuca NullPointerException
  • 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 lub long, to wykonywany jest test równości całkowitej; jeśli promowany Typ to float ordouble ' ` to test równości zmiennoprzecinkowej to wystąpili

Zauważ, ż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

Podobne pytania

Podobne pytania

 125
Author: polygenelubricants,
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.

 11
Author: Alexander Pogrebnyak,
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.

 3
Author: Damian Leszczyński - Vash,
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."

 3
Author: supercat,
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.

 1
Author: perdian,
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

 1
Author: Joachim Sauer,
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