Класс исключения напрямую наследует Throwable, не являясь подклассом RuntimeException или Error. Как компилятор классифицирует такое исключение?
Такое исключение считается проверяемым. Метод, который может его выбросить, должен обработать его или указать в throws; принадлежность к проверяемым определяется не тем, является ли класс прямым наследником Throwable, а отсутствием наследования от RuntimeException или Error.
Иерархия Throwable разделяет условия, которые приложение обычно обязано обработать, и аварийные состояния, для которых обязательная декларация считалась бы чрезмерной. Проверяемые исключения позволяют обнаруживать часть ошибок взаимодействия между методами на этапе компиляции.
При этом Java оставляет возможность создавать собственные классы исключений непосредственно от Throwable. Такой класс не получает статус непроверяемого только из-за близости к корню иерархии.
Неверная классификация влияет на контракт метода. Если считать прямого наследника Throwable непроверяемым, можно пропустить обязательную обработку или объявление исключения, и код не скомпилируется.
Обратная ошибка тоже опасна: попытка обрабатывать каждый Error как обычную прикладную ошибку приводит к неуместному восстановлению после состояний вроде нехватки памяти. Поэтому важно различать положение класса в иерархии, а не ориентироваться только на его имя или непосредственного родителя.
В Java непроверяемыми считаются RuntimeException, Error и все их подклассы. Все остальные подклассы Throwable являются проверяемыми, включая пользовательский класс, который напрямую наследует Throwable.
Компилятор анализирует объявленные и потенциально выбрасываемые проверяемые исключения. Для ProtocolFailure метод call обязан иметь throws ProtocolFailure либо перехватить исключение внутри себя, а вызывающий метод run обязан обработать его или тоже объявить в throws.
Если тот же класс унаследовать от RuntimeException, он станет непроверяемым: объявление throws формально не потребуется. Если унаследовать его от Error, результат также будет непроверяемым, хотя назначение Error обычно связано с серьёзными сбоями виртуальной машины или среды выполнения, а не с обычными бизнес-ошибками.
catch (Throwable) способен перехватить и проверяемые, и непроверяемые исключения, поскольку Throwable является их общим предком. Однако это не меняет классификацию конкретного класса и не отменяет требований к throws в местах, где проверяемое исключение может быть выброшено.
Прямое наследование от Throwable технически допустимо, но обычно хуже выражает намерение, чем наследование от Exception или специализированного подкласса. Выбор RuntimeException оправдан, когда вызывающий код не обязан явно восстанавливать контракт, а проверяемое исключение — когда обработка является значимой частью взаимодействия между методами.
В библиотеке обмена сообщениями нужно сообщить вызывающему коду о нарушении протокола. Команда сначала создала исключение напрямую от Throwable, ожидая, что оно будет универсальным и не потребует обязательного объявления.
Вариант с наследованием от Throwable технически работает, но делает исключение проверяемым и заставляет добавить throws или catch во всех соответствующих вызовах. Вариант с RuntimeException упрощает сигнатуры, но позволяет случайно проигнорировать ошибку протокола.
Выбран специализированный подкласс Exception, например ProtocolException. Он сохраняет проверяемую семантику, ясно передаёт ожидаемый контракт библиотеки и не смешивает прикладную ошибку с аварийными состояниями Error. В результате компилятор помогает не забыть обработку нарушения протокола, а иерархия остаётся понятной для сопровождения.
Throwable, но называется RuntimeFailure?Нет. Имя класса не влияет на классификацию. Важна только цепочка наследования: если в ней нет RuntimeException или Error, класс является проверяемым.
Throwable непроверяемым при перехвате через catch (Throwable)?Нет. catch определяет, какой тип обработчик способен принять, но не меняет свойства самого класса. Обработка через catch (Throwable) допустима, однако исходное исключение всё равно остаётся проверяемым в местах его потенциального выброса.
throws Throwable, чтобы корректно описать метод, который выбрасывает такой класс?Да, это удовлетворяет требованию компилятора, потому что Throwable является супертипом пользовательского исключения. Но контракт становится слишком широким: вызывающий код формально должен учитывать также Exception и Error как возможные типы. Поэтому обычно лучше объявлять конкретный тип или его осмысленного общего предка.