Можно ли преобразовать значение null в int?
Я знаю, что когда я прочитаю ответ на это, я увижу, что я что-то упустил
это было у меня под глазами. Но последние 30 минут я пытался выяснить это сам
без результата.
Итак, я писал программу на Java 6 и обнаружил некоторые (для меня) странные функции.
Чтобы попытаться изолировать его, я сделал два небольших примера.
Сначала я попробовал следующий метод:
private static int foo()
{
return null;
}
и компилятор отказался от него: Тип несоответствия: невозможно преобразовать из null в int.
Это хорошо со мной, и он уважает семантику Java, с которой я знаком.
Затем я попробовал следующее:
private static Integer foo(int x)
{
if (x < 0)
{
return null;
}
else
{
return new Integer(x);
}
}
private static int bar(int x)
{
Integer y = foo(x);
return y == null ? null : y.intValue();
}
private static void runTest()
{
for (int index = 2; index > -2; index--)
{
System.out.println("bar(" + index + ") = " + bar(index));
}
}
Это компиляция без ошибок! Но, на мой взгляд, должна быть ошибка преобразования типа
в строке
return y == null ? null : y.intValue();
Если я запустил программу, я получаю следующий вывод:
bar(2) = 2
bar(1) = 1
bar(0) = 0
Exception in thread "main" java.lang.NullPointerException
at Test.bar(Test.java:23)
at Test.runTest(Test.java:30)
at Test.main(Test.java:36)
Можете ли вы объяснить это поведение?
Обновление
Большое спасибо за многие разъясняющие ответы. Я был немного обеспокоен, потому что
этот пример не соответствовал моей интуиции. Одна вещь, которая меня беспокоила, была
что null был преобразован в int, и мне было интересно, какой результат
be: 0 как в С++? Это было бы очень странно.
Хорошо, что преобразование невозможно во время выполнения (исключение нулевого указателя).
Ответы
Ответ 1
Посмотрите на строку:
return y == null ? null : y.intValue();
В операторе ? :
обе стороны :
должны иметь один и тот же тип. В этом случае Java будет иметь тип Integer
. Integer
может быть null
, поэтому левая сторона в порядке. Выражение y.intValue()
имеет тип int
, но Java будет автоматически помещать его в значение Integer
(обратите внимание, вы могли бы просто написать y
, который сохранил бы вам это автобокс).
Теперь результат должен быть снова распакован на int
, так как возвращаемый тип метода int
. Если вы удалите Integer
null
, вы получите NullPointerException
.
Примечание: Параграф 15.25 Спецификации языка Java объясняет точные правила преобразования типов в отношении условного оператора ? :
.
Ответ 2
Guava имеет довольно элегантное решение для этого, используя MoreObjects.firstNonNull
:
Integer someNullInt = null;
int myInt = MoreObjects.firstNonNull(someNullInt, 0);
Ответ 3
Тип возвращаемого типа выводится здесь Java. Это проблема..
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.25
Вот реальная проблема -
Если один из второго и третьего операндов имеет нулевой тип, а тип другого - ссылочный тип, то тип условного выражения - это ссылочный тип.
Таким образом, в основном компилятор отображает возвращаемый тип условного выражения как целое, и поэтому он позволяет успешно скомпилировать.
EDIT: см. правила в комментариях
Ответ 4
Это иллюстрирует проблемную разницу между тем, как читает код человека и компилятор читает код.
Когда вы видите тройственное выражение, вы можете мысленно разделить его на две части в стиле выражения if
/else
:
if (y == null)
return null;
else
return y.intValue();
Вы можете видеть, что это недопустимо, поскольку это приводит к возможной ветке, где метод, определенный для возврата int
, фактически возвращает null
(незаконно!).
Что видит компилятор - это выражение, которое должно иметь тип. Он отмечает, что тройная операция включает в себя a null
с одной стороны и a int
с другой; из-за поведения autoboxing Java, тогда возникает "лучшее предположение" (мой термин, а не Java) относительно того, какой тип выражения: Integer
(это справедливо: это единственный тип, который может быть юридически null
или в коробке int
).
Так как метод должен возвращать int
, это отлично от перспективы компилятора: возвращаемое выражение оценивается как Integer
, которое может быть автоматически распаковано.
Ответ 5
На всякий случай, если вы не используете Guava в своем проекте, но уже используете Apache Commons, вы можете использовать Apache Lang3 с его ObjectUtils класс.
Использование в основном такое же, как у Guava:
Integer number = null;
int notNull = ObjectUtils.firstNonNull(number, 0);
Обратите внимание, что этот метод в библиотеке Guava работает быстрее, чем в Apache.
Вот короткое сравнение, которое я только что сделал на своем ноутбуке (Core i7-7500U 2,7 ГГц), Oracle Java 8, несколько запусков, предварительный подогрев JVM, результаты усредняются:
╔══════════════╦══════╦══════╦════════╦══════╗
║ Library/Runs ║ 1000 ║ 1mln ║ 100mln ║ 1bln ║
╠══════════════╬══════╬══════╬════════╬══════╣
║ Apache ║ 1 ║ 30 ║ 782 ║ 9981 ║
║ Guava ║ 1 ║ 22 ║ 120 ║ 828 ║
╚══════════════╩══════╩══════╩════════╩══════╝
Результаты приведены в миллисекундах.
Я не думаю, что вам часто нужно запускать этот метод в миллиарды раз, но, тем не менее, всегда полезно сравнивать производительность
Ответ 6
Проблема с autounboxing null
значениями может быть очень раздражающей. В вашем примере это комбинация вывода типа тернарного оператора, выводящего и autounboxing (с JLS следует проконсультироваться, почему он ведет себя так)
Но в целом вы должны стараться избегать использования типов обертки. Используйте int
вместо Integer
. Если вам нужно специальное значение, означающее "нет результата", вы можете использовать Integer.MAX_VALUE
, например.
Ответ 7
этот компилирует
private static int foo()
{
return (Integer)null;
}