Подтвердить что ты не робот

Почему законно повторно бросать Throwable в определенных случаях, не объявляя об этом?

Я ожидаю, что следующий код поднимет ошибку времени компиляции на throw t;, потому что main не объявляется throw Throwable, но он успешно компилируется (в Java 1.7.0_45) и производит вывод вы ожидали бы, если бы эта ошибка компиляции была исправлена.

public class Test {
    public static void main(String[] args) {
        try {
            throw new NullPointerException();

        } catch(Throwable t) {
            System.out.println("Caught "+t);
            throw t;
        }
    }
}

Он также компилируется, если Throwable изменен на Exception.

Это не скомпилируется, как ожидалось:

public class Test {
    public static void main(String[] args) {
        try {
            throw new NullPointerException();

        } catch(Throwable t) {
            Throwable t2 = t;
            System.out.println("Caught "+t2);
            throw t2;
        }
    }
}

Это компилируется:

public class Test {
    public static void main(String[] args) {
        try {
            throwsRuntimeException();

        } catch(Throwable t) {
            System.out.println("Caught "+t);
            throw t;
        }
    }

    public static void throwsRuntimeException() {
        throw new NullPointerException();
    }
}

Это не означает:

public class Test {
    public static void main(String[] args) {
        try {
            throwsCheckedException();

        } catch(Throwable t) {
            System.out.println("Caught "+t);
            throw t;
        }
    }

    public static void throwsCheckedException() {
        throw new java.io.IOException();
    }
}

Это также компилируется:

public class Test {
    public static void main(String[] args) throws java.io.IOException {
        try {
            throwsIOException();

        } catch(Throwable t) {
            System.out.println("Caught "+t);
            throw t;
        }
    }

    public static void throwsIOException() throws java.io.IOException {
        throw new java.io.IOException();
    }
}

Более сложный пример - проверенное исключение ловутся внешним блоком catch, вместо того, чтобы быть объявленным броском. Это компилируется:

public class Test {
    public static void main(String[] args) {
        try {
            try {
                throwsIOException();

            } catch(Throwable t) {
                System.out.println("Caught "+t);
                throw t;
            }
        } catch(java.io.IOException e) {
            System.out.println("Caught IOException (outer block)");
        }
    }

    public static void throwsIOException() throws java.io.IOException {
        throw new java.io.IOException();
    }
}

Таким образом, кажется, что есть специальный случай, позволяющий реконструировать исключения, когда компилятор может определить, что пойманное исключение всегда является законным для повторного броска. Это верно? Где это указано в JLS? Существуют ли какие-либо другие неясные угловые случаи, подобные этому?

4b9b3361

Ответ 1

Это охватывается JLS 11.2.2 (выделено мной):

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

  • E - это класс исключений, который может выставить блок try оператора try, который объявляет C; и

  • E - это присвоение, совместимое с любым из классов catch catch exception; и

(...)

Другими словами, E, тип, на который ссылается в doc, - это тип, который может быть выброшен, а не тип параметра catch catch, который его ловит (класс catchable exception). Это просто должно быть назначение, совместимое с параметром catch, но тип параметра не используется в анализе.

Вот почему вы можете сказать окончательный или эффективный окончательный параметр исключения - если t в вашем примере был переназначен, анализ выйдет из окна.

Ответ 2

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

Обратите внимание, что это правда с Java 7, если я не ошибаюсь.

Ответ 3

Когда вы ловите Throwable или Exception, и эта переменная является фактически окончательной, вы можете перебросить одну и ту же переменную, и компилятор будет знать, какие отмеченные исключения вы могли бы добавить в блок try {} catch.