Что произойдёт при завершении try with resources, если выражение ресурса вернуло null?

Что произойдёт при завершении try-with-resources, если выражение ресурса вернуло null?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Если выражение ресурса в try-with-resources вернуло null, блок try продолжит выполнение, а неявный вызов close() для этого ресурса пропустится. Исключение из-за попытки закрыть null не возникнет.

Исторический контекст

Конструкция try-with-resources появилась в Java 7, чтобы автоматически освобождать ресурсы при нормальном завершении блока и при исключениях. Она заменила повторяющийся шаблон с ручным вызовом close() в finally, где легко было забыть проверку на null или скрыть исходную ошибку исключением при закрытии.

Постановка проблемы

Ресурс может быть необязательным: например, фабрика возвращает null, если внешний источник недоступен или конфигурация отключает его. При ручном закрытии прямой вызов close() у такой ссылки привёл бы к NullPointerException.

Неверное предположение о поведении try-with-resources может привести либо к лишней проверке и усложнению кода, либо к ошибочной диагностике: отсутствие вызова close() при null — это нормальное поведение конструкции, а не подавленная ошибка.

Подробное решение

Перед закрытием каждого ресурса Java проверяет, не равна ли ссылка null. Если ссылка ненулевая, вызывается её close(); если равна null, закрытие пропускается. Поэтому null считается отсутствием созданного ресурса, который нужно освобождать.

class Demo { static AutoCloseable resource() { return null; } public static void main(String[] args) throws Exception { try (AutoCloseable r = resource()) { System.out.println("body"); } } }

В этом примере будет выведено body, а исключение при завершении блока не возникнет. Переменная r всё равно доступна внутри try, но содержит null.

Это правило относится именно к автоматическому закрытию ресурса. Если внутри тела явно вызвать метод через ссылку r, возникнет NullPointerException. Если само выражение resource() выбросит исключение, присваивание ресурса не завершится успешно; это уже ошибка инициализации, а не случай null.

Для нескольких ресурсов каждый успешно инициализированный ненулевой ресурс закрывается в обратном порядке объявления. Ресурсы, чьи ссылки равны null, при этом просто пропускаются. Исключения от реального close() обрабатываются по обычным правилам try-with-resources, включая механизм подавленных исключений.

Ситуация из практики

Сервис получает необязательный аудит-логгер: при отключённом аудите фабрика возвращает null, а при включённом — объект, реализующий AutoCloseable. Вариант с ручным finally требует отдельной проверки ссылки и аккуратного сохранения исходного исключения; без этого ошибка закрытия может замаскировать ошибку бизнес-операции.

Можно заранее заменить null на объект-заглушку, что упрощает последующий код, но добавляет лишний объект и требует корректной реализации его методов. Можно отказаться от try-with-resources и закрывать ресурс вручную, но это увеличивает вероятность ошибки. Выбранный вариант — try-with-resources с возможным null: он использует стандартную семантику Java, не создаёт лишних объектов и безопасно пропускает закрытие отсутствующего ресурса.

Что кандидаты часто упускают

  1. Означает ли null успешное создание ресурса?

Нет. null означает, что ссылка не указывает на объект, поэтому закрывать нечего. Try-with-resources не создаёт ресурс автоматически и не превращает null в специальный объект-заглушку.

  1. Будет ли вызван close(), если метод возвращает null, но тип результата — AutoCloseable?

Нет. Проверяется фактическое значение ссылки, а не её объявленный тип. Тип должен позволять компилятору использовать ресурс, но при значении null вызов close() пропускается.

  1. Можно ли считать try-with-resources защитой от любого NullPointerException, связанного с ресурсом?

Нет. Конструкция защищает только этап автоматического закрытия null-ресурса. Обращение к тому же ресурсу внутри тела блока, например вызов его метода, по-прежнему приводит к NullPointerException, если ссылка равна null.