Поймать исключение и выбросить его, но это не исключение - PullRequest
10 голосов
/ 16 октября 2019

Я наткнулся на код, похожий на этот:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() {
    throw new RuntimeException();
}

Этот код удивляет меня, потому что он выглядит так, как будто run() -метод способен генерировать Exception, так как он ловит Exceptionи затем сбрасывает его, но метод не объявлен, чтобы бросить Exception и, очевидно, не должен быть. Этот код прекрасно компилируется (по крайней мере, в Java 11).

Я ожидаю , что мне придется объявить throws Exception в run() -методе.

Дополнительная информация

Аналогичным образом, если объявлено doSomething для выброса IOException, то в методе run() должно быть объявлено только IOException, дажехотя 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 никогда не выбрасывается).

1 Ответ

0 голосов
/ 17 октября 2019

Я не сканировал JLS, как вы задали в своем вопросе, поэтому, пожалуйста, примите этот ответ с крошкой соли. Я хотел сделать это комментарием, но он был бы слишком большим.


Иногда я нахожу забавным, как javac довольно "умна" в некоторых случаях (как в вашем случае),но оставляет много других вещей, которые будут обработаны позже JIT. В этом случае просто компилятор "может сказать", что будет пойман только RuntimeException. Это очевидно, это единственное, что вы добавляете в doSomething. Если вы слегка измените свой код на:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

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

Но все далеко от идеала, вы можете «обмануть» компилятор еще раз с помощью:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        ex2 = ex;
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

IMO, поскольку ex2 = ex; он не должен снова потерпеть неудачу, нооно делает.

На всякий случай это было скомпилировано с javac 13+33

...