Почему выборочный блок 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"s close() throws Exception с close() который не бросает никаких исключений)?

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

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

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

}

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

Заранее спасибо.

4 ответа

Решение

Это необязательно, если 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 это проверенное исключение.

Вы можете просто выбросить исключение (или перехватить его в другом блоке 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");
    }
}

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

Пример:

public class AutoClosableDemo
{

    public static void main( final String[] args )
    {
        try (MyAutoCloseable1 mac1 = new MyAutoCloseable1())
        {
            System.out.println( "try-with-resource MyAutoCloseable1" );
        }
        try (MyAutoCloseable2 mac2 = new MyAutoCloseable2())
        {
            System.out.println( "try-with-resource MyAutoCloseable2" );
        }
        // The following is not allowed, because
        // "Unhandled exception type Exception thrown by automatic close() invocation on mac3"
        // try (MyAutoCloseable3 mac3 = new MyAutoCloseable3())
        // {
        // System.out.println( "try-with-resource MyAutoCloseable13" );
        // }
        System.out.println( "done" );
    }

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

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

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

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

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

Try-with-resources всегда (неявно) вызывает метод close.

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

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

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

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

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

Да, close() может выдать исключение, но попытка с ресурсами справится и с этим.

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

Так что эта попытка с ресурсами не нуждается в подвохе.

Другие вопросы по тегам