Я наткнулся на код, который выглядит примерно так:
void run() {
try {
doSomething();
} catch (Exception ex) {
System.out.println("Error: " + ex);
throw ex;
}
}
void doSomething() {
throw new RuntimeException();
}
Этот код удивляет меня, потому что он выглядит так, как будто run()
-method способен генерировать Exception
, так как он перехватывает Exception
и затем перебрасывает его, но метод не объявлен как throw Exception
и, очевидно, не нуждается в этом. Этот код прекрасно компилируется (по крайней мере, в Java 11).
Я ожидал бы, что мне придется объявить throws Exception
в run()
методе.
Дополнительная информация
Аналогичным образом, если doSomething
объявлено, что оно выбрасывает, IOException
тогда IOException
нужно только объявить в run()
-методе, даже если Exception
он пойман и переброшен.
void run() throws IOException {
try {
doSomething();
} catch (Exception ex) {
System.out.println("Error: " + ex);
throw ex;
}
}
void doSomething() throws IOException {
// ... whatever code you may want ...
}
Вопрос
Java обычно любит ясность, в чем причина такого поведения? Это всегда было так? Что в спецификации языка Java позволяетrun()
методу не объявлять throws Exception
в приведенных выше фрагментах кода? (Если я добавлю это, IntelliJ предупредит меня, что Exception
никогда не бросается).
javac
- я сталкивался со случаями, когда Eclipse-компилятор был более снисходительным.-source 1.6
флагом вызывает ошибку компиляции, как и ожидалось. Компиляция с исходным кодом совместимости 7 никак не поднимет ошибку компиляцииIn detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catch block, the compiler verifies that the type of the rethrown exception meets the following conditions : 1. 1. The try block is able to throw it. 2. There are no other preceding catch blocks that can handle it. 3. It is a subtype or supertype of one of the catch clause's exception parameters.
Ответы:
Я не просканировал,
JLS
как вы задали в своем вопросе, поэтому, пожалуйста, ответьте на этот вопрос солью. Я хотел сделать это комментарием, но это было бы слишком большим.Время от времени я нахожу забавным, как
javac
довольно «умный» в некоторых случаях (как в вашем случае), но оставляет много других вещей, которые будут обработаны позжеJIT
. В этом случае просто компилятор "может сказать", чтоRuntimeException
будет пойман только a . Это очевидно, это единственное, что вы добавляетеdoSomething
. Если вы слегка измените свой код на:вы увидите другое поведение, потому что теперь
javac
можете сказать, что есть новое,Exception
которое вы бросаете, не связанное с тем, которое вы поймали.Но все далеко от идеала, вы можете «обмануть» компилятор еще раз с помощью:
ИМО, из-за
ex2 = ex;
этого не должно снова подвести, но это так.На всякий случай это было скомпилировано с
javac 13+33
источник
ex2
будет сгенерировано исключение, он изначально был создан как,Exception
но затем переназначенex
, и поэтому компилятор не может быть умным.JLS
может прийти тот, у кого есть страсть, и предоставит необходимые цитаты, чтобы доказать это; к сожалению у меня их нет.ex = ex;
), эвристика больше не применяется. Такое поведение, кажется, применяется для всех уровней источника с 7 по 11 и, вероятно, 13