Почему выборочно блокировка try-with-resources выборочно необязательна?

Я читал, что блок catch в try-with-resources не является обязательным. Я попытался создать объект Connection в блоке try-with-resources без последующего блока catch, чтобы получить ошибку компилятора от eclipse: "Необработанный тип исключения SQLException, созданный с помощью автоматического вызова close().

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

Есть ли какое-то особое требование, чтобы конкретная реализация AutoCloseable не объявляла прямое исключение в методе close()? (например, переопределить AutoCloseable close() throws Exception с помощью close(), которая не выбрасывает исключение)?

.. или это возможно просто проблема затмения?

Изменить: здесь самый простой фрагмент кода, который все еще вызывает проблему:

try (Connection con = dataSource.getConnection()) {
  /*...*/

}

Мысли о том, связано ли это с использованием источника данных JNDI?

Спасибо заранее.

Ответ 1

Необязательно, если close() не может выставить проверенное исключение. Однако, если close() может, тогда проверенное исключение должно обрабатываться обычным образом либо с блоком catch, либо путем выброса из метода, в котором находится блок try-with-resources.

Подробнее см. JLS 14.2.3

14.20.3.2. Расширенные попытки использования ресурсов

Оператор try-with-resources с по меньшей мере одним предложением catch и/или предложением finally называется расширенным приложением try-with-resources.

Значение расширенного оператора try-with-resources:

try ResourceSpecification
    Block
[Catches]
[Finally]

задается следующим переводом на базовый оператор try-with-resources, вложенный в команду try-catch или try-finally или try-catch-finally:

try {
    try ResourceSpecification
       Block
}
[Catches]
[Finally]

Эффект перевода заключается в том, чтобы указать спецификацию ресурса "внутри" инструкции try. Это позволяет заключить предложение catch расширенного оператора try-with-resources для исключения исключения из-за автоматической инициализации или закрытия любого ресурса.

Кроме того, все ресурсы будут закрыты (или будут закрыты) к моменту завершения блока finally в соответствии с намерением ключевого слова finally.

Мысли о том, связано ли это с использованием источника данных JNDI?

Да, это так.

В примере, который вы указали в блоке try-with-resourses, необходимо поймать исключение и обработать или выбросить из метода, в котором находится блок, поскольку SQLException является проверенным исключением.

Ответ 2

Вы можете просто выбросить исключение (или поймать его в другом блоке try-catch):

private static void test() throws IOException {
    try(InputStream is = new FileInputStream("test.txt")) {
        while(is.read() > -1) {
        }
    } finally {
        // Will get executed, even if exception occurs
        System.out.println("Finished");
    }
}

Ответ 3

Не каждый класс Java (!) генерирует исключение. Иногда вы просто хотите использовать try-with-resources для использования функции автоматического закрытия, и ничего больше.

BufferedReader br = new BufferedReader(new FileReader(path));
try {
    return br.readLine();
} finally {
    if (br != null) br.close();
}

Этот улов является необязательным, поскольку readLine() не выбрасывает исключение (проверено).

Да, функция close() может генерировать исключение, но обработчики try-in-resources тоже обрабатывают.

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
} 

Таким образом, этот try-with-resources не нуждается в catch.

Ответ 4

Вы можете проверить JLS, но на самом деле относительно легко рассуждать, почему это единственный правильный способ, которым должен вести себя язык.

Основным правилом проверенных исключений является то, что любое проверенное исключение, объявленное методом, должно обрабатываться либо путем его улова, либо путем вызова метода вызова.

При попытке использовать ресурсы всегда (неявно) вызывает метод close.

Итак, если конкретный метод закрытия AutoClosable, который вы используете (определяемый типом, объявленным в try), объявляет о том, чтобы выкинуть проверенное исключение, такое как SQLException, вам нужно обработать это исключенное исключение где-то, иначе можно было бы нарушить правило!

Если метод close не объявляет, что он выбрал исключенное исключение, это правило не нарушается, и вам не нужно обрабатывать проверенное исключение для неявного вызова метода close. Это на самом деле сбой компиляции, если вы пытаетесь поймать проверенное исключение, которое никогда не объявляется, чтобы его выбрали.

Ответ 5

Вы можете создать AutoClosable, который не требует явного catch-блока для его метода close(), объявив его как RuntimeException. RuntimeException (в отличие от отмеченных исключений) не требуется для перехвата компилятором.

Это полезно, если метод close() вашего AutoClosable не выдает никаких исключений.

Пример:

public class AutoClosableDemo
{
    public static void main( final String[] args )
    {
        try (MyAutoCloseable mac = new MyAutoCloseable())
        {
            System.out.println( "Hello world!" );
        }
        System.out.println( "done" );
    }

    public static class MyAutoCloseable implements AutoCloseable {
        @Override
        public void close() throws RuntimeException {
            System.out.println("MyAutoCloseable.close()");
        }
    }
}

Если вы измените сигнатуру метода MyAutoCloseable.close() на public void close() throws Exception тогда компилятор потребует, чтобы вы перехватили это Exception явно.