Ошибка компилятора блока catch для Java
Почему в Java мы можем поймать Exception
, даже если оно не выбрано, но мы не можем его поймать (кроме "unchecked" RuntimeException
и его подклассы). Пример кода:
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
}
}
}
Любые идеи?
Ответы
Ответ 1
A RuntimeException
может быть выброшен любым кодом. Другими словами, компилятор не может легко предсказать, какой код может его бросить. A RuntimeException
может быть захвачен блоком catch(Exception e)
.
IOException
, однако, является проверенным исключением - только вызовы метода, объявленные для его броска, могут это сделать. Компилятор может (разумно) уверен, что это невозможно, если только не вызовы методов, которые объявлены для его броска.
Компилятор Java просто не учитывает ситуацию "нет кода вообще в рамках блока try" - он всегда позволяет вам улавливать непроверенные исключения, так как во всех разумных сценариях будет существовать код, который потенциально может вызвать неконтролируемое исключение.
Из раздел 14.21 JLS:
Блок catch C доступен, если выполняются оба значения:
- Некоторый оператор выражения или throw в блоке try доступен и может генерировать исключение, тип которого присваивается параметру предложения catch. (Выражение считается достижимым, если внутреннее утверждение, содержащее его, доступно.)
- В заявлении try нет ранее блока catch A, так что тип параметра C совпадает с подклассом типа параметра A.
Возможно, компилятор должен понять, что в вашем первом случае нет выражений в блоке try... похоже, что это все еще недостижимое предложение catch, для меня.
EDIT: Как отмечено в комментариях, раздел 14.20 содержит следующее:
Это ошибка времени компиляции, если предложение catch
ловит проверенный тип исключения E1, но не существует типа исключенного исключения E2, для которого выполняются все следующие операции:
- E2 <: E1
- Блок
try
, соответствующий предложению catch
, может вызывать E2 - Не предшествует
catch
блок сразу включающего оператора try захватывает E2 или супертип E2.
если E1 не является исключением класса.
Итак, похоже, что вы на самом деле фол, но спецификация не такая ясная, как это могло бы быть в плане недостижимых блоков catch в 14.21.
Ответ 2
IO Исключения могут быть обнаружены только в том случае, если компилятор предсказывает, что в коде может быть что-то, что вызывает IOException. Таким образом, вы получаете предупреждение о том, что исключение IO никогда не выбрасывается из тела оператора try (так как в теле попытки нет ничего).
Ответ 3
Вы не можете перехватывать исключенные проверенные исключения, потому что их нельзя выбросить. Вы можете поймать Exception
, потому что исключенное исключение во время выполнения IS a Exception
и потенциально могут быть сброшены.
Ответ 4
Поскольку для отмеченных исключений метод, который их выбрасывает, должен явно указывать этот факт ключевым словом 'throws', поэтому, если в вашем блоке нет 'throws IOException', у компилятора есть информация, это невозможно для исключения IOException, поэтому все, что вы делаете после ловли, было бы недостижимым.
Ответ 5
IOException
- это проверенное исключение, которое вызывается только кодом, связанным с IO. Поскольку ваш блок try ничего не делает, ничто не связано с IO, IOExceptions никогда не будут выбрасываться, поэтому блокировки catch никогда не будут выполняться, и компилятор не позволит вам обойтись с ним.
Как вы сказали, исключение может относиться к неконтролируемым исключениям времени выполнения, которые могут произойти в любой момент. Это основное различие между отмененными и проверенными исключениями, и поэтому компилятор не применяет код для улавливания всех возможных исключений во время выполнения.
Ответ 6
Просто Java предполагает, что любая строка кода может вызывать общий Exception
или Throwable
, т.е. OutOfMemoryException
, который является Error
скорее an Exception
. То же самое относится к NPE.
IOException
- это конкретное исключение, которое может быть выбрано только управляемым кодом, поэтому, если у вас нет вызовов ввода/вывода в вашем блоке catch, ваш компилятор не сможет его поймать.
Просто, чтобы сравнить с С# world, в С# такой код будет скомпилирован, но будет концептуальной ошибкой, так как если вы ничего не сделаете, вы не дойдете до блока catch. Инструмент, такой как ReSharper, может предупредить вас об этом.