Java недоступна ошибка компилятора блока catch - PullRequest
16 голосов
/ 05 мая 2011

Почему в Java мы можем поймать Exception, даже если он не выброшен, но мы не можем поймать его подкласс (за исключением "непроверенных" RuntimeException s и его подклассов). Пример кода:

class Test {
    public static void main(String[] args) {
        try {
            // do nothing
        } catch (Exception e) {
            // OK           
        }

        try {
            // do nothing
        } catch (IOException e) {
               // COMPILER ERROR: Unreachable catch block for IOException.
               //This exception is never thrown from the try statement body
        }       
    }
}

Есть идеи?

Ответы [ 6 ]

25 голосов
/ 05 мая 2011

A RuntimeException может быть сгенерировано любым кодом. Другими словами, компилятор не может легко предсказать, какой код может его генерировать. RuntimeException может быть перехвачен блоком catch(Exception e).

IOException, однако, является проверенным исключением - это могут делать только вызовы методов, которые объявлены как выброшенные Компилятор может быть (разумно) уверен, что это не может произойти, если нет вызовов метода, которые объявлены для его выброса.

Компилятор Java просто не учитывает ситуацию «кода вообще нет в блоке try» - он всегда позволяет перехватывать непроверенные исключения, поскольку во всех разумных сценариях будет код, который может потенциально генерировать непроверенное исключение.

Из секции 14.21 JLS:

Блок захвата C доступен, если выполняются оба следующих условия:

  • Некоторые выражения или оператор throw в блоке try достижимы и могут генерировать исключение, тип которого можно назначить параметру предложения catch C. (Выражение считается достижимым, если самый внутренний оператор, содержащий его, достижим.)
  • В операторе try нет более раннего блока catch A, так что тип параметра C такой же, как или подкласс типа параметра A.

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

РЕДАКТИРОВАТЬ: Как отмечено в комментариях, раздел 14.20 содержит это:

Ошибка времени компиляции, если предложение catch перехватывает проверенный тип исключения E1 , но не существует проверенного типа исключения E2 , так что выполняются все следующие условия:

  • E2 <: <em>E1
  • Блок try, соответствующий предложению catch, может выдать E2
  • Нет предшествующего catch блока непосредственно включенного оператора try, перехватывает E2 или супертип E2 .

если E1 не является исключением класса.

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

5 голосов
/ 12 февраля 2014

Исключения ввода-вывода могут быть перехвачены, только если компилятор предсказывает, что в коде может быть что-то, генерирующее исключение IOException.Таким образом, вы получаете предупреждение о том, что исключение ввода-вывода никогда не выдается из тела оператора try (поскольку в теле команды try ничего нет).

3 голосов
/ 05 мая 2011

Вы не можете поймать нецененные проверенные исключения, потому что они не могут быть выброшены. Вы можете поймать Exception, потому что непроверенное исключение времени выполнения является Exception и МОЖЕТ быть сгенерировано.

3 голосов
/ 05 мая 2011

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

1 голос
/ 05 мая 2011

IOException - это проверенное исключение, которое генерируется только кодом, связанным с IO. Поскольку ваш блок try ничего не делает, ничего, связанного с IO, никогда не произойдет, исключения IOException никогда не будут выброшены, поэтому блок catch никогда не будет выполнен, и компилятор не позволит вам обойти это. Как вы сказали, исключение может относиться к непроверенным исключениям времени выполнения, которые могут возникнуть в любой момент. Это основное различие между непроверенными и проверенными исключениями, и именно поэтому компилятор не заставляет код перехватывать все возможные исключения во время выполнения.

0 голосов
/ 05 мая 2011

Просто Java предполагает, что любая строка кода может генерировать общий Exception или Throwable, т.е. OutOfMemoryException, что является Error, а не Exception. То же самое относится и к NPE.

IOException - это конкретное исключение, которое может быть вызвано только управляемым кодом, поэтому, если у вас нет вызовов ввода-вывода в блоке catch, ваш компилятор не сможет его перехватить.

Просто для сравнения с миром C # в C # такой код будет компилироваться, но это будет концептуальной ошибкой, поскольку, если вы ничего не сделаете, вы не достигнете блока catch. Такой инструмент, как ReSharper, может предупредить вас об этом.

...