Почему компилятор разрешает броски, когда метод никогда не выдаст исключение

Мне интересно, почему компилятор java допускает выбросы в объявлении метода, когда метод никогда не вызовет исключение. Потому что "throws" - это способ обработки исключения (указание вызывающей стороне обработать его).

Так как есть два способа обработки исключений (throws и try/catch). В try/catch он не позволяет перехватывать исключение, которое не было сгенерировано в блоке try, но допускает выбросы в методе, который не может вызвать исключение.

private static void methodA() {
    try {
        // Do something
        // No IO operation here
    } catch (IOException ex) {  //This line does not compile because
                              //exception is never thrown from try
        // Handle   
    }
}

private static void methodB() throws IOException { //Why does this //compile when excetion is never thrown in function body
    //Do Something 
    //No IO operation
}

Ответ 1

Предложение throws является частью контракта метода. Он требует, чтобы вызывающий метод вел себя так, как если бы указанное исключение могло быть вызвано методом (т.е. Либо перехватить исключение, либо объявить свое собственное предложение throws).

Возможно, что первоначальная версия метода не генерирует исключение, указанное в предложении throws, но будущая версия может выбросить его, не нарушая API (т.е. Любой существующий код, вызывающий метод, все равно будет проходить компиляцию).

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

Первый пример:

Предположим, у вас есть этот код, который использует methodB:

private static void methodA() {
    methodB(); // doesn't have throws IOException clause yet
}

Если позже вы захотите изменить methodB для methodB IOException, methodA остановит прохождение компиляции.

Второй пример:

Предположим, у вас есть этот код, который использует methodB:

private static void methodA() {
    try {
        methodB(); // throws IOException
    }
    catch (IOException ex) {

    }
}

Если вы удалите предложение throws из будущей версии methodB, methodA больше не будет проходить компиляцию.

Этот пример не очень интересен, когда methodA является private, потому что он может использоваться только локально (внутри того же класса, где легко изменить все методы, которые его вызывают).

Однако, если он становится public, вы не знаете, кто использует (или будет использовать) ваш метод, поэтому у вас нет контроля над всем кодом, который может сломаться в результате добавления или удаления предложения throws.

И если это метод экземпляра, есть еще одна причина для разрешения предложения throws, даже если вы не выбрасываете исключение - метод может быть переопределен, а переопределяющий метод может вызвать исключение, даже если реализация базового класса этого не делает.

Ответ 2

Потому что подпись определяет контракт метода. Даже если метод не генерирует IOException сейчас, возможно, это произойдет в будущем, и вы хотите подготовиться к такой возможности.

Предположим, что вы просто предоставили фиктивную реализацию для этого метода, но вы знаете, что позже фактическая реализация потенциально вызовет IOException. Если компилятор помешает вам добавить это предложение throws, вы будете вынуждены заново обработать все вызовы (рекурсивно) для этого метода, как только вы предоставите фактическую реализацию метода.

Ответ 3

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

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

Ответ 4

  1. MethodB генерирует IOException, поэтому метод, вызывающий methodB, отвечает за перехват исключения, которое будет вызвано methodB. Попробуйте вызвать methodB из других методов, он попросит вас перехватить его или повторно выдать IOException. Где-то вам придется перехватывать IOException в цепочке (в блоке try/catch). Так что вы не получите ошибку времени компиляции.

    private void sampleMethod() {try {methodB(); } catch (IOException e) {//TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) {//TODO Автоматически сгенерированный блок catch e.printStackTrace(); }}

  2. try/catch в methodA по общему признанию проглатывает исключение, это означает, что methodA отвечает за перехват исключения в блоке try/catch. "Каждый оператор в любой Java-программе должен быть достижимым, т.е. Каждый оператор должен быть выполнимым хотя бы один раз". Таким образом, вы получите ошибку компилятора, так как ваш блок try не имеет кода, вызывающего IOException.