Почему сравнение Integer с int может вызвать исключение NullPointerException в Java?
мне было очень сложно наблюдать эту ситуацию:
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
...
}
Итак, как я думаю, сначала выполняется операция бокса (т. е. java пытается извлечь значение int из null) и операция сравнения имеет более низкий приоритет, поэтому возникает исключение.
вопрос: почему это реализовано таким образом в Java? Почему бокс имеет более высокий приоритет, чем сравнение ссылок? Или почему они не реализовали проверку против null до бокса?
на данный момент это выглядит непоследовательным, когда NullPointerException бросается с обернутыми примитивами и не бросается с правда типы объектов.
6 ответов:
Короткий Ответ:
ключевой момент заключается в следующем:
==между двумя ссылочными типами всегда сравнение ссылок
- чаще всего, например, с
IntegerиString, вы хотели бы использоватьequalsвместо==между ссылочным типом и числовым примитивным типом всегда числовое сравнение
- тип ссылки будет подвергнут распаковке преобразования
- анбоксинг
nullвсегда бросаетNullPointerException- в то время как Java имеет много специальных процедур для
String, это на самом деле не примитивный типприведенные выше утверждения справедливы для любого заданного действительный Java-код. При таком понимании в представленном фрагменте нет никакой несогласованности.
Длинный Ответ
здесь представлены актуальные ПСБ разделы:
операторы равенства ссылок JLS 15.21.3
==и!=если операнды оператора равенства имеют либо ссылочный тип, либо null тип, то операция является равенство объектов.
это объясняет следующее:
Integer i = null; String str = null; if (i == null) { // Nothing happens } if (str == null) { // Nothing happens } if (str == "0") { // Nothing happens }оба операнда являются ссылочными типами, и вот почему
==ссылка равенства сравнение.это также объясняет следующее:
System.out.println(new Integer(0) == new Integer(0)); // "false" System.out.println("X" == "x".toUpperCase()); // "false"на
==численное равенство,по крайней мере один из операндов должен быть числового типа:JLS 15.21.1 операторы числового равенства
==и!=если операнды оператора равенства и числового типа, или один числового типа и другой кабриолет к числовому типу, бинарное числовое продвижение выполнено на операндах. Если повышенный тип операндов
intилиlong, затем выполняется тест на целочисленное равенство; если повышенный типfloat ordouble`, затем выполняется тест на равенство с плавающей запятой.обратите внимание, что двоичное числовое продвижение выполняет преобразование набора значений и преобразование распаковки.
это объясняет:
Integer i = null; if (i == 0) { //NullPointerException }вот выдержка от эффективное Java 2-е издание, пункт 49: предпочитайте примитивы коробочным примитивам:
таким образом, используйте примитивы в предпочтении к коробочному примитиву всякий раз, когда у вас есть выбор. Примитивные типы проще и быстрее. Если вы должны использовать коробочные примитивы, будьте осторожны! Автобоксинг уменьшает многословие, но не опасность использования коробочных примитивов. Когда ваша программа сравнивает два коробочных примитива с
==оператор, он делает сравнение идентичности, это почти наверняка не то, что вы хотите. Когда ваша программа выполняет вычисления смешанного типа с использованием коробочных и распакованных примитивов, она выполняет распаковку, а когда ваша программа выполняет распаковку, она может броситьNullPointerException. Наконец, когда ваша программа содержит примитивные значения, это может привести к дорогостоящим и ненужным созданию объектов.есть места, где у вас нет выбора, кроме как использовать коробочные примитивы, например дженерики, но в противном случае вы должны серьезно рассмотреть, если решение использовать коробку примитивов является обоснованным.
ссылки
- JLS 4.2. Примитивные типы и значения
- " The числовые типы являются целочисленными типами и типами с плавающей запятой."
- преобразование распаковки JLS 5.1.8
- " тип, как говорят,преобразовать в числовой тип если это числовой тип, или это ссылочный тип, который может быть преобразуется в числовой тип путем распаковки преобразования."
- " распаковка преобразование преобразует [...] от типа
Integerтипаint"- "если
rиnull, преобразования распаковывания бросает!--9-->"- Java Language Guide / Autoboxing
- JLS 15.21.1 операторы числового равенства
==и!=- операторы равенства ссылок JLS 15.21.3
==и!=- JLS 5.6.2 Binary Numeric Promotion
вопросы
- при сравнении двух
Integersв Java происходит автоматическая распаковка?- почему эти
==а неequals()?- Ява: какая разница между автоупаковка и литья?
обзоры вопросы
- в чем разница между int и integer в Java и в C#?
- гарантируется ли, что новое целое число(i) == i в Java? (да!!! Коробка распакована, а не наоборот!)
- почему
int num = Integer.getInteger("123")броситьNullPointerException? (!!!)- Java noob: дженерики только над объектами? (да, к сожалению)
- Java
String.equalsversus==
ваш пример NPE эквивалентен этому коду, благодаря автоупаковка:
if ( i.intValue( ) == 0 )отсюда NPE if
iиnull.
if (i == 0) { //NullPointerException ... }i-целое число, а 0-int, поэтому в том, что действительно делается, что-то вроде этого
i.intValue() == 0и это вызывает nullPointer, потому что i равно null. Для String у нас нет этой операции, поэтому здесь нет исключения.
создатели Java могли бы определить
==оператор для непосредственного воздействия на операнды разных типов, в этом случае задаетсяInteger I; int i;сравнениеI==i;может задать вопрос " делаетIдержите ссылку наIntegerстоимость которого составляетi?- ...вопрос, на который можно было ответить без труда, даже когдаIимеет значение null. К сожалению, Java напрямую не проверяет, равны ли операнды разных типов; вместо этого он проверяет, позволяет ли язык тип любого операнда, который будет преобразован в тип другого, и-если это произойдет-сравнивает преобразованный операнд с неконвертированным. Такое поведение означает, что для переменныхx,yиzС некоторыми комбинациями типов, можно иметьx==yиy==zноx!=z[например, x=16777216f y=16777216 z=16777217]. Это также означает, что сравнениеI==iпереводится как " преобразовать I вintи, если это не вызывает исключение, сравните его сi."
Это из-за Javas автоупаковка характеристика. Компилятор обнаруживает, что в правой части сравнения вы используете примитивное целое число и должны распаковать целочисленное значение оболочки в примитивное значение int.
потому что это не возможно (он имеет значение null, как вы растянулся на земле) в
NullPointerExceptionбросается.
на
i == 0Java попытается выполнить автоматическую распаковку и выполнить числовое сравнение (т. е. " это значение, хранящееся в объекте оболочки, на который ссылаетсяiто же самое, что и значение0?").С
iиnullраспаковка будет бросатьNullPointerException.рассуждение идет так:
первое предложение JLS § 15.21.1 операторы числового равенства == и != звучит так:
если операнды оператор равенства оба имеют числовой тип, или один имеет числовой тип, а другой преобразуется (§5.1.8) в числовой тип, двоичное числовое продвижение выполняется на операндах (§5.6.2).
явно
iпреобразуется в числовой тип и0- это числовой тип, поэтому двоичное числовое продвижение выполняется над операндами.§ 5.6.2 Бинарное Числовое Продвижение говорит (среди прочего):
если из операндов ссылочного типа выполняется распаковка преобразования (§5.1.8).
§ 5.1.8 Преобразования Распаковывания говорит (среди прочего):
если r имеет значение null, преобразования распаковывания бросает!--5-->
Comments