Давайте рассмотрим ситуацию, когда у вас есть код:
public void delete() throws IOException, SQLException { // Non-Compliant
/* ... */
}
Опасность здесь в том, что код, который вы пишете для вызова, delete()
будет выглядеть так:
try {
foo.delete()
} catch (Exception e) {
/* ... */
}
Это тоже плохо. И это будет поймано с другим правилом, что флаги перехватывают базовый класс Exception.
Ключ в том, чтобы не писать код, который заставляет вас хотеть писать плохой код в другом месте.
Правило, с которым вы сталкиваетесь, является довольно распространенным. Checkstyle имеет это в своих правилах дизайна:
ThrowsCount
Restricts выбрасывает операторы на указанное количество (1 по умолчанию).
Обоснование: исключения являются частью интерфейса метода. Объявление метода для выброса слишком большого количества исключений с разными корнями делает обременительной обработку исключений и приводит к плохим практикам программирования, таким как написание кода, такого как catch (Exception ex). Эта проверка вынуждает разработчиков помещать исключения в иерархию, так что в простейшем случае вызывающая сторона должна проверять только один тип исключений, но любые подклассы могут быть специально перехвачены при необходимости.
Это точно описывает проблему, и в чем проблема, и почему вы не должны это делать. Это общепринятый стандарт, который многие инструменты статического анализа будут определять и отмечать.
И хотя вы можете делать это в соответствии с языковым дизайном, и могут быть моменты, когда это правильно делать, это то, что вы должны увидеть и сразу же сказать: «Почему я это делаю?» Это может быть приемлемо для внутреннего кода, где каждый достаточно дисциплинирован, чтобы никогда catch (Exception e) {}
, но чаще всего я видел, как люди срезают углы, особенно во внутренних ситуациях.
Не заставляйте людей, использующих ваш класс, писать плохой код.
Я должен отметить, что важность этого уменьшается в Java SE 7 и более поздних версиях, потому что один оператор catch может перехватывать несколько исключений ( Catching Multiple Exception Type and Rethrowing Exception с улучшенной проверкой типов из Oracle).
В Java 6 и более ранних версиях вы получите код, который выглядит следующим образом:
public void delete() throws IOException, SQLException {
/* ... */
}
а также
try {
foo.delete()
} catch (IOException ex) {
logger.log(ex);
throw ex;
} catch (SQLException ex) {
logger.log(ex);
throw ex;
}
или же
try {
foo.delete()
} catch (Exception ex) {
logger.log(ex);
throw ex;
}
Ни один из этих вариантов с Java 6 не идеален. Первый подход нарушает СУХОЙ . Несколько блоков делают одно и то же, снова и снова - по одному разу для каждого исключения. Вы хотите записать исключение и сбросить его? Хорошо. Те же строки кода для каждого исключения.
Второй вариант хуже по нескольким причинам. Во-первых, это означает, что вы ловите все исключения. Нулевой указатель попадает туда (и не должен). Кроме того, вы перебрасываете значение, Exception
которое означает, что сигнатура метода будет таким, deleteSomething() throws Exception
что просто создаст беспорядок в стеке, так как люди, использующие ваш код, теперь вынуждены это делать catch(Exception e)
.
С Java 7 это не так важно, потому что вместо этого вы можете сделать:
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
Кроме того, проверка типов, если таковые имеются, перехватывает типы создаваемых исключений:
public void rethrowException(String exceptionName)
throws IOException, SQLException {
try {
foo.delete();
} catch (Exception e) {
throw e;
}
}
Проверка типов распознает, что e
могут быть только типы IOException
или SQLException
. Я до сих пор не испытываю особого энтузиазма по поводу использования этого стиля, но он не вызывает такого плохого кода, как это было в Java 6 (где это заставит вас иметь сигнатуру метода как суперкласс, который расширяют исключения).
Несмотря на все эти изменения, многие инструменты статического анализа (Sonar, PMD, Checkstyle) все еще применяют руководства в стиле Java 6. Это не плохо. Я склонен согласиться с предупреждением о том, что они по- прежнему будут применяться, но вы можете изменить их приоритет на майор или минор в зависимости от того, как ваша команда их расставляет по приоритетам.
Если исключения должны быть проверены или незарегистрированный ... это вопрос г р е с т дебаты , которые можно легко найти бесчисленное количество сообщений в блоге , принимая каждую сторону аргумента. Однако, если вы работаете с проверенными исключениями, вам, вероятно, следует избегать создания нескольких типов, по крайней мере, в Java 6.