Почему в этом случае разрешен проверенный тип исключения?

Я случайно заметил, что этот оператор throw (извлеченный из более сложного кода) компилирует:

void foo() {
    try {

    } catch (Throwable t) {
        throw t;
    }
}

В течение короткого, но счастливого момента я подумал, что проверенные исключения, наконец, решили просто умереть уже, но это все еще вызывает возмущение:

void foo() {
    try {

    } catch (Throwable t) {
        Throwable t1 = t;
        throw t1;
    }
}

Блок try не должен быть пустым; кажется, что он может иметь код, пока этот код не выдает проверенное исключение. Это кажется разумным, но мой вопрос в том, какое правило в спецификации языка описывает это поведение? Насколько я могу судить, §14.18 Заявление о выбросе явно запрещает его, поскольку тип выражения t является проверенным исключением, и он не пойман или не объявлен брошенным. (?)

Ответ 1

Я думаю, что формулировка в §14.18 The throw Заявление, что вы имеете в виду, это ошибка в JLS - текст, который должен быть обновлен с Java SE 7, и не было.

Бит текста JLS, описывающий предполагаемое поведение, приведен в п. 11.2.2. Исключение анализа выражений:

Оператор throw чье выраженное выражение является окончательным или эффективным окончательным параметром исключения в предложении catch, может вызывать класс исключений E iff:

  • Е класс исключения, что try блок try заявления, которое декларирует C может бросить; а также
  • E - присвоение, совместимое с любым классом исключаемых классов C; а также
  • E не является присвоением, совместимым с любым из захватывающих классов исключений из предложений catch объявленных слева от C в том же самом заявлении try.

Первая маркерная точка является релевантной; потому что параметр catch -clause t является окончательным (это означает, что он никогда не был назначен или не увеличивался или не уменьшался, см. §4.12.4 final Variables), throw t может только бросать то, что может быть выбрано блоком try.

Но, как вы говорите, проверка времени компиляции в §14.18 не делает за это никаких санкций. §11.2.2 не определяет, что разрешено, а что нет; скорее, он должен быть анализом последствий различных ограничений на то, что можно бросить. (Этот анализ действительно возвращается к более нормативным частям спецификации - сам он использует его во второй точке маркера - но в §14.18 нельзя просто сказать "это ошибка времени компиляции, если она выдает исключение, которое она не может бросок за §11.2.2 ", потому что это будет круговой.)

Поэтому я считаю, что §14.18 необходимо скорректировать с учетом целей § 11.2.2.

Хорошая находка!

Ответ 2

Это связано с тем изменением, которое было включено в Project Coin, введенное в Java 7, чтобы обеспечить общую обработку исключений с повторным копированием исходного исключения. Вот пример, который работает в Java 7, но не в Java 6:

public static demoRethrow() throws IOException {
    try {
        throw new IOException("Error");
    }
    catch(Exception exception) {
        /*
         * Do some handling and then rethrow.
         */
        throw exception;
    }
}

Вы можете прочитать всю статью, объясняющую изменения здесь.

Ответ 3

Это поведение подробно описано в JLS в 11.2. Проверка времени исключения компиляции:

Оператор throw чье выраженное выражение является окончательным или эффективным окончательным параметром исключения в предложении catch, может вызывать класс исключений E iff:

  • Е класс исключения, что try блок try заявления, которое декларирует C может бросить; а также

  • E - присвоение, совместимое с любым классом исключаемых классов C; а также

  • E не является присвоением, совместимым с любым из захватывающих классов исключений из предложений catch объявленных слева от C в том же самом заявлении try.

(Акцент мой.)

Ваш второй пример завершился неудачей, поскольку t1 не является "параметром исключения для предложения catch ".