Подтвердить что ты не робот

Можно ли преобразовать значение 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 как в С++? Это было бы очень странно. Хорошо, что преобразование невозможно во время выполнения (исключение нулевого указателя).

4b9b3361

Ответ 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;
}