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

Повторное исключение: почему метод компилируется без предложения бросков?

В исходном коде ниже я перестраиваю Exception.
Почему нет необходимости размещать ключевое слово throws в сигнатуре метода?

public void throwsOrNotThrowsThatsTheQuestion() {
    try {

        // Any processing

    } catch (Exception e) {
        throw e;
    }
}
4b9b3361

Ответ 1

Такое поведение появляется только на Java 1.7. При компиляции с 1.6 я получаю следующее сообщение об ошибке компилятора:

c:\dev\src\misc>javac -source 1.6 Main.java
warning: [options] bootstrap class path not set in conjunction with -source 1.6
Main.java:22: error: unreported exception Exception; must be caught or declared
to be thrown
        throw e;
        ^
1 error
1 warning

Но с Java 1.7 он компилируется.

c:\dev\src\misc>javac -source 1.7 Main.java

c:\dev\src\misc>

... Пока я не выброшу Exception в блок try:

public static void throwsOrNotThrowsThatsTheQuestion() {
try {

    // Any processing
    throw new IOException("Fake!");

} catch (Exception e) {
    throw e;
}

Компиляция...

c:\dev\src\misc>javac -source 1.7 Main.java
Main.java:22: error: unreported exception IOException; must be caught or declare
d to be thrown
        throw e;
        ^
1 error

Похоже, что Java 1.7 достаточно умен, чтобы обнаружить тип Exception (s), который может быть брошен, анализируя блок-код try, где в 1.6 только что увидел throw e; типа Exception и дал ошибка только для этого.

Изменив его, чтобы выбросить RuntimeException, он скомпилировал его, как и ожидалось, потому что, как всегда, unchecked Exception не требует предложения throws:

public static void throwsOrNotThrowsThatsTheQuestion() {
try {

    // Any processing
    throw new RuntimeException("Fake!");

} catch (Exception e) {
    throw e;
}

Компиляция...

c:\dev\src\misc>javac -source 1.7 Main.java

c:\dev\src\misc>

Объяснение

Вот что происходит:

В Java 7 введена более инклюзивная проверка типов. Цитирование...

Рассмотрим следующий пример:

static class FirstException extends Exception { }
static class SecondException extends Exception { }

public void rethrowException(String exceptionName) throws Exception {
  try {
    if (exceptionName.equals("First")) {
      throw new FirstException();
    } else {
      throw new SecondException();
    }
  } catch (Exception e) {
    throw e;
  }
}

В этом примере try block может вызвать либо FirstException, либо SecondException. Предположим, вы хотите указать эти типы исключений в предложении throws объявления метода rethrowException. В выпусках до Java SE 7 вы не можете этого сделать. Поскольку параметр исключения в предложении catch, e, является типом Exception, а блок catch восстанавливает параметр исключения e, вы можете указать только тип исключения Exception в предложении throws декларации метода rethrowException.

Однако в Java SE 7 вы можете указать типы исключений FirstException и SecondException в предложении throws в объявлении метода rethrowException. Компилятор Java SE 7 может определить, что исключение, вызванное выражением throw, должно быть получено из блока try, и единственными исключениями, которые были выбраны блоком try, может быть FirstException и SecondException. Несмотря на то, что параметр исключения в предложении catch, e, является типом Exception, компилятор может определить, что он является экземпляром либо FirstException, либо SecondException:

(акцент мой)

public void rethrowException(String exceptionName)
throws FirstException, SecondException {
  try {
    // ...
  }
  catch (Exception e) {
    throw e;
  }
}

Ответ 2

java.lang.Exception - проверенное исключение, поэтому это не сработает или даже не скомпилируется. Он будет работать с незащищенным (java.lang.RuntimeException). Совершенно бесполезно, вы бросаете исключение внутри блока catch или нет.

Ошибка компилятора будет выглядеть примерно так (в зависимости от компилятора):

java: незарегистрированное исключение java.lang.Exception; должен быть пойман или объявлен брошенным

EDIT: Java 7 может справиться с такими ситуациями, если вы никогда не выбрали исключение

Ответ 3

Если вы проверяете исключенное исключение, вам нужно иметь его в списке бросков

public void retrhowChecked() throws Exception {
    try {
        throw new IOException();
    } catch(Exception e) {
        throw e;
    }
}

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

public void retrhowUnchecked() {
    try {
        throw new IOException();
    } catch(Exception e) {
        throw new RuntimeException(e);
    }
}

Подробнее об исключениях здесь.

Ответ 4

Почему нет необходимости помещать ключевое слово throw в метод подпись?

Вы можете указать, что ваш // Any processing не выбрасывает исключение.

Пример:

Этот компилируемый файл.

public void throwsOrNotThrowsThatsTheQuestion() {
    try {

        throw new RuntimeException();

    } catch (Exception e) {
        throw e;
    }

Это не будет компилироваться, вам нужно добавить предложение throws.

public void throwsOrNotThrowsThatsTheQuestion() {
    try {
        throw new Exception();
    } catch (Exception e) {
        //do something like log and rethrow
        throw e;
    }
}

Работает с java 7. В предыдущей версии генерируется исключение. Дополнительная информация rethrow в java 7

Ответ 5

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

Но если в методе уже включен блок catch catch, то не требуется декларация thorws, поскольку обрабатывается исключение, вызываемое этим методом.

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

Надеюсь, это очистит ваши сомнения.

Ответ 6

throw new Exception(); - это то, что вы никогда не должны делать в catch блок, но вам может понадобиться или хотите сделать throw new SomeException(throwable); (сохраняя полную трассировку стека) вместо throw throwable;, чтобы соответствовать API вашего метода, например когда он объявляет бросить SomeException, но вы вызываете код который может вызвать IOException, который вы не хотите добавлять к нему метод throws.

Вероятно, наиболее распространенным случаем является новое RuntimeException (throwable); в избегайте наличия предложения бросков.