Рассмотрите ситуацию: при вызове метода с параметром примитивного типа ему передают null через ссылку-оболочку. На каком этапе и почему возникает ошибка?
Ошибка возникает во время выполнения, до входа в тело метода: Java пытается автоматически распаковать ссылку-оболочку в примитив и выбрасывает NullPointerException. Значение null не может быть преобразовано в примитив, поэтому вызов метода не завершается успешно.
Автоупаковка и автораспаковка появились в Java 5 вместе с обобщениями. Обобщённые коллекции работают только со ссылочными типами, поэтому для хранения чисел пришлось использовать оболочки вроде Integer вместо int.
Чтобы разработчику не приходилось явно преобразовывать примитивы в объекты и обратно, компилятор получил возможность автоматически вставлять такие преобразования. Это упростило код, но добавило риск скрытых исключений и дополнительных операций.
Ссылка типа Integer может содержать либо объект с числом, либо null. Примитивный тип int состояния null не имеет.
Если значение оболочки передаётся туда, где требуется int, компилятор вставляет автораспаковку. При null она завершается исключением до выполнения вызываемого метода, поэтому обработка проблемы внутри этого метода невозможна.
На этапе компиляции Java рассматривает передачу Integer в параметр int как необходимость вызвать операцию распаковки, эквивалентную получению примитивного значения из объекта. Фактически вызов метода получает не null, а результат этой операции.
Для ненулевой ссылки распаковка возвращает примитивное значение. Для null попытка получить значение оболочки приводит к NullPointerException. Это происходит во время вычисления аргумента, до передачи управления методу.
Автораспаковка может быть неочевидной: она возникает не только в аргументах методов, но и при присваивании оболочки примитиву, арифметических операциях и сравнении примитива с оболочкой. Поэтому nullable-значения от базы данных, JSON или внешних API нельзя без проверки напрямую использовать в выражениях, ожидающих примитив.
Надёжный подход — явно определить политику обработки null: заменить его значением по умолчанию, отклонить как некорректный ввод или сохранить отсутствие значения в ссылочном типе. Автоматическая распаковка удобна, но скрывает потенциальную точку отказа и не должна заменять валидацию данных.
Сервис получает из базы число повторных попыток как Integer: для старых записей поле может быть null. Внутренний метод планировщика принимает int, поэтому прямой вызов иногда завершался NullPointerException ещё до запуска планирования.
Рассматривались варианты:
null, но переносит проверку глубже в бизнес-логику;null нулём — просто, но может скрыть ошибку данных;null исключением в точке чтения — делает контракт строгим, но требует корректной обработки старых записей;Выбрали последний вариант: значение нормализуется сразу после чтения, а дальше используется примитив. Это зафиксировало бизнес-правило, исключило неожиданное исключение в планировщике и сохранило простой контракт внутреннего метода.
Нет, если тип выражения совместим с требуемым примитивом через автораспаковку. Компилятор корректно строит программу, поскольку Integer в общем случае может содержать число. Невозможность распаковать конкретное значение null обнаруживается только во время выполнения, если статический анализатор вроде инструмента проверки nullability не выявит риск заранее.
Нет. Копирование ссылки между переменными типа Integer не требует автораспаковки, поэтому null спокойно сохраняется. Исключение появится позже — в первой операции, где понадобится примитив, например при передаче в параметр int, арифметике или присваивании переменной типа int.
null важнее неявного значения по умолчанию?Потому что Java не выбирает бизнес-смысл отсутствующего значения. Для счётчика null может означать ноль, неизвестное состояние, ошибку загрузки или отсутствие настройки. Если полагаться на автораспаковку, результатом будет техническое исключение; если явно обработать значение, программа фиксирует выбранное правило и делает поведение проверяемым.