Розглянемо ситуацію, коли ви надали код:
public void delete() throws IOException, SQLException { // Non-Compliant
/* ... */
}
Небезпека тут полягає в тому, що код, який ви пишете на дзвінок, delete()
матиме вигляд:
try {
foo.delete()
} catch (Exception e) {
/* ... */
}
Це теж погано. І буде спіймано ще одне правило, згідно з яким прапори ловлять базовий клас винятку.
Головне - не писати код, який змушує вас писати поганий код в іншому місці.
Правило, з яким ви стикаєтесь, є досить поширеним. У Checkstyle це є свої правила дизайну:
ThrowCount
Обмежує кидає висловлювання до визначеного рахунку (1 за замовчуванням).
Обґрунтування: Винятки становлять частину інтерфейсу методу. Оголошення методу для викидання занадто багатьох винятків з різними коренями робить обробку винятків обтяжливою і призводить до поганих практик програмування, таких як написання коду, як улов (Exception ex). Ця перевірка змушує розробників вводити винятки в таку ієрархію, що в найпростішому випадку абонент повинен перевірити лише один тип винятку, але будь-які підкласи можуть бути спеціально зафіксовані, якщо це необхідно.
Це точно описує проблему, в чому полягає проблема, і чому ви не повинні цього робити. Це добре прийнятий стандарт, що багато інструментів статичного аналізу будуть ідентифікувати та позначити.
І хоча ви можете робити це відповідно до мовного дизайну, і можуть бути випадки, коли це правильно робити, це ви повинні побачити і негайно піти "гм, чому я це роблю?" Це може бути прийнятним для внутрішнього коду, де кожен достатньо дисциплінований, щоб ніколи catch (Exception e) {}
, але частіше за все я не бачив, як люди вирізали куточки, особливо у внутрішніх ситуаціях.
Не змушуйте людей, які користуються вашим класом, бажати писати неправильний код.
Я мушу зазначити, що важливість цього питання зменшується за допомогою Java SE 7 і пізніше, оскільки в одній заяві вилову можуть входити декілька винятків ( Ловля декількох типів винятків та повторне вилучення винятків з покращеною перевіркою типів від 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.