Мне было очень непонятно наблюдать за этой ситуацией:
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
выбрасывается с обернутыми примитивами и не генерируется с истинными типами объектов.
Ответы:
Краткий ответ
Ключевой момент заключается в следующем:
==
между двумя ссылочными типами всегда выполняется сравнение ссылокInteger
иString
, вы хотите использоватьequals
вместо==
между ссылочным типом и числовым примитивным типом всегда числовое сравнениеnull
всегда выкидываетNullPointerException
String
, на самом деле это НЕ примитивный тип.Приведенные выше утверждения справедливы для любого допустимого кода Java. С таким пониманием в представленном вами фрагменте нет никаких противоречий.
Длинный ответ
Вот соответствующие разделы JLS:
Это объясняет следующее:
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"
Чтобы
==
быть числовым равенством, по крайней мере один из операндов должен быть числового типа :Это объясняет:
Integer i = null; if (i == 0) { //NullPointerException }
Вот выдержка из Effective Java 2nd Edition, пункт 49: Предпочитайте примитивы упакованным примитивам :
Есть места, где у вас нет другого выбора, кроме как использовать упакованные примитивы, например дженерики, но в противном случае вам следует серьезно подумать, оправдано ли решение использовать упакованные примитивы.
Рекомендации
Integer
в типint
»r
естьnull
, распаковка преобразование бросаетNullPointerException
»==
и!=
==
и!=
Связанные вопросы
Integers
в Java происходит автоматическая распаковка?==
а нетequals()
?Связанные вопросы
int num = Integer.getInteger("123")
бросаетNullPointerException
? (!!!)String.equals
против==
источник
someRef == 0
всегда используется числовое сравнение, это очень разумный выбор, поскольку сравнение ссылок двух упакованных примитивов почти всегда является ошибкой программиста. В этом случае было бы бесполезно по умолчанию ссылаться на сравнения.(myInteger == 0)
на(myInteger != null && myInteger == 0)
вместо того, чтобы полагаться на разработчика, который напишет этот шаблонный код проверки нуля? ИМО, я должен быть в состоянии проверить,if (myBoolean)
и это должно оцениватьсяtrue
тогда и только тогда, когда базовое значение конкретноtrue
- мне не нужно сначала проверять значение нуля.Ваш пример NPE эквивалентен этому коду благодаря автобоксу :
if ( i.intValue( ) == 0 )
Следовательно, NPE, если
i
естьnull
.источник
if (i == 0) { //NullPointerException ... }
i - это целое число, а 0 - это целое число, поэтому в действительности делается что-то вроде этого
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
».источник
==
таким образом , что , если во всех случаях , когдаx==y
,y==z
, иx==z
все компилируется без жалобы, три сравнения будут вести себя как отношение эквивалентности. Любопытно, что дизайнеры используют всевозможные причудливые языковые функции, но игнорируют аксиоматику.Это из-за автобокса Javas функции . Компилятор обнаруживает, что в правой части сравнения вы используете примитивное целое число, и ему также необходимо распаковать значение Integer обертки в примитивное значение int.
Поскольку это невозможно (это ноль, как вы обозначили),
NullPointerException
выбрасывается.источник
В
i == 0
Java попытается выполнить автоматическую распаковку и выполнить численное сравнение (т.е. "- это значение, хранящееся в объекте-оболочке, на которое ссылаетсяi
то же, что и на значение0
?»).Так как
i
этоnull
распаковка будет бросатьNullPointerException
.Рассуждения звучат так:
Первое предложение JLS § 15.21.1 Операторы числового равенства == и! = Читается так:
Очевидно, что он
i
может быть преобразован в числовой тип и0
является числовым типом, поэтому двоичное числовое продвижение выполняется для операндов.В § 5.6.2 Рекламное продвижение двоичных чисел (среди прочего) говорится:
§ 5.1.8 Преобразование распаковки говорит (среди прочего):
источник
Просто напишите метод и вызовите его, чтобы избежать исключения NullPointerException.
public static Integer getNotNullIntValue(Integer value) { if(value!=null) { return value; } return 0; }
источник