Могу ли я перехватить несколько исключений Java в одном предложении catch?

703

В Java я хочу сделать что-то вроде этого:

try {
    ...     
} catch (/* code to catch IllegalArgumentException, SecurityException, 
            IllegalAccessException, and NoSuchFieldException at the same time */) {
   someCode();
}

...вместо:

try {
    ...     
} catch (IllegalArgumentException e) {
    someCode();
} catch (SecurityException e) {
    someCode();
} catch (IllegalAccessException e) {
    someCode();
} catch (NoSuchFieldException e) {
    someCode();
}

Есть какой-либо способ сделать это?

froadie
источник

Ответы:

1134

Это стало возможным начиная с Java 7 . Синтаксис для блока multi-catch:

try { 
  ...
} catch (IOException | SQLException ex) { 
  ...
}

Помните, однако, что если все исключения принадлежат одной и той же иерархии классов, вы можете просто перехватить этот базовый тип исключения.

Также обратите внимание, что вы не можете перехватить и ExceptionA, и ExceptionB в одном и том же блоке, если ExceptionB наследуется, прямо или косвенно, от ExceptionA. Компилятор будет жаловаться:

Alternatives in a multi-catch statement cannot be related by subclassing
  Alternative ExceptionB is a subclass of alternative ExceptionA
OscarRyz
источник
81
TT - зачем переопределять оператор bitwise or( |)? Почему бы не использовать запятую или оператор, имеющий более похожее значение, logical or( ||)?
ArtOfWarfare
11
@ArtOfWarfare Может быть, они подумали, что это уже не будет иметь значения после того, как они уже разработали синтаксис множественных границ для дженериков.
JimmyB
12
Знак XOR (I) не совпадает со знаком OR (||), A | B означает либо A, либо B, но не оба A || B означает либо A, либо B, либо оба, так что для исключений это либо исключение A, либо исключение B, но не оба одновременно. Вот почему они использовали XOR sing вместо OR, и вы можете ясно видеть, что когда исключение - броски, если вы добавили 2 исключения, одно из них является
подтипом
41
@ user1512999 в Java, побитовое значение XOR равно ^ (каретка), а побитовое ИЛИ равно | (труба) docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
Льюис
6
Стоит отметить, что тип исключения, перехваченного в блоке multi-catch, оценивается для самого производного общего родителя
yanpas
104

Не совсем до Java 7, но я бы сделал что-то вроде этого:

Java 6 и раньше

try {
  //.....
} catch (Exception exc) {
  if (exc instanceof IllegalArgumentException || exc instanceof SecurityException || 
     exc instanceof IllegalAccessException || exc instanceof NoSuchFieldException ) {

     someCode();

  } else if (exc instanceof RuntimeException) {
     throw (RuntimeException) exc;     

  } else {
    throw new RuntimeException(exc);
  }

}



Java 7

try {
  //.....
} catch ( IllegalArgumentException | SecurityException |
         IllegalAccessException |NoSuchFieldException exc) {
  someCode();
}
user454322
источник
11
Обратите внимание, что ваш пример Java 6 нарушает способность компилятора определять, что будет выброшено откуда.
MichaelBlume
2
@MichaelBlume Правда, что не так плохо. Вы всегда можете получить оригинальное исключение с exc.getCause(). В качестве примечания, Роберт К. Мартин (среди прочих) рекомендует использовать непроверенные исключения (компилятор не имеет представления о том, какие исключения будут выбрасываться оттуда); обратитесь к главе 7: Обработка ошибок в его книге « Чистый код» .
user454322
4
В вашем примере с Java 6 не следует ли перебрасывать исходное исключение вместо создания нового экземпляра исключения, т.е. throw excвместо throw new RuntimeException(exc)?
Дэвид ДеМар
5
Это довольно плохая практика с точки зрения читабельности.
Раджеш Дж Адвани
3
Экземпляр операции немного дорог, лучше избегать как можно больше.
Парамеш Корракути
23

В Java 7 вы можете определить несколько предложений catch, таких как:

catch (IllegalArgumentException | SecurityException e)
{
    ...
}
crusam
источник
16

Если существует иерархия исключений, вы можете использовать базовый класс для перехвата всех подклассов исключений. В вырожденном случае вы можете перехватить все исключения Java с помощью:

try {
   ...
} catch (Exception e) {
   someCode();
}

В более распространенном случае, если RepositoryException является базовым классом, а PathNotFoundException является производным классом, тогда:

try {
   ...
} catch (RepositoryException re) {
   someCode();
} catch (Exception e) {
   someCode();
}

Приведенный выше код будет перехватывать RepositoryException и PathNotFoundException для одного вида обработки исключений, а все остальные исключения объединяются. Начиная с Java 7, согласно ответу @ OscarRyz выше:

try { 
  ...
} catch( IOException | SQLException ex ) { 
  ...
}
Михаил Шопсин
источник
7
BTW предложения catch обрабатываются по порядку, поэтому, если вы помещаете родительский класс исключений перед дочерним классом, он никогда не вызывается, например: try {...} catch (Exception e) {someCode (); } catch (RepositoryException re) {// никогда не достигался}
Майкл Шопсин
4
На самом деле именно потому, что он никогда не будет достигнут, такой код даже не компилируется.
полигенасмазочные материалы
15

Нет, по одному на каждого клиента.

Вы можете поймать суперкласс, такой как java.lang.Exception, если вы выполняете одно и то же действие во всех случаях.

try {
    // some code
} catch(Exception e) { //All exceptions are caught here as all are inheriting java.lang.Exception
    e.printStackTrace();
}

Но это не может быть лучшей практикой. Вы должны ловить исключение только тогда, когда у вас есть стратегия для его фактической обработки - а регистрация и повторная обработка не являются «обработкой». Если у вас нет корректирующего действия, лучше добавьте его в сигнатуру метода и дайте всплыть кому-то, кто может справиться с ситуацией.

duffymo
источник
20
Могу ли я попросить вас перефразировать часть о ловле java.lang.Exception? Я понимаю, что это пример, но я чувствую, что некоторые люди могут прочитать этот ответ и сказать: «О, хорошо, тогда я просто поймаю исключение», когда это, вероятно, не то, что они хотят (или должны) сделать.
Роб Хруска
2
Я знал об этом, но я не хочу этого делать ... О, ну, думаю, я застрял с 4 уловами тогда, до следующей версии Java ...
froadie
@duffymo: Что не так с ведением журнала и повторным броском? За исключением того, что он загромождает код, его эквивалент не перехватывает его, не так ли. Рассматривается с точки зрения общей стратегии обработки ошибок. Что плохо, так это регистрация, а не повторное выбрасывание.
Фрэнк Остерфельд
5
Я не рассматриваю ведение журнала и повторную обработку чего-либо. Я предпочел бы позволить этому пузыриться кому-то, кто может сделать что-то значимое. Последний слой, где исключения никогда не должны выходить (например, контроллеры в веб-приложении), должен регистрировать ошибку в этом случае.
duffymo
Я единственный, кто находит абсурдным, что журнал не генерируется автоматически для меня? Кажется, мы все должны писать одно и то же глупое сообщение в журнале каждый раз, когда какой-то фрагмент кода может вызвать исключение.
ArtOfWarfare
10

Более чистая (но менее многословная и, возможно, не столь предпочтительная) альтернатива ответу пользователя user454322 на Java 6 (т. Е. На Android) будет заключаться в том, чтобы перехватывать все Exceptions и перебрасывать RuntimeExceptions. Это не сработает, если вы планируете перехватывать другие типы исключений дальше по стеку (если только вы не перебрасываете их), но будет эффективно перехватывать все проверенные исключения.

Например:

try {
    // CODE THAT THROWS EXCEPTION
} catch (Exception e) {
    if (e instanceof RuntimeException) {
        // this exception was not expected, so re-throw it
        throw e;
    } else {
        // YOUR CODE FOR ALL CHECKED EXCEPTIONS
    } 
}

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

Олег Васкевич
источник
1
Этот подход не позволяет компилятору определить, будет ли доступен «блок захвата».
the_new_mr
3

В до 7 как насчет:

  Boolean   caught = true;
  Exception e;
  try {
     ...
     caught = false;
  } catch (TransformerException te) {
     e = te;
  } catch (SocketException se) {
     e = se;
  } catch (IOException ie) {
     e = ie;
  }
  if (caught) {
     someCode(); // You can reference Exception e here.
  }
Билл С
источник
3
будет хорошим решением. Как насчет вставки окончательного контроля caughtв finallyблоке?
Andrea_86
Это требует больше строк, чем оригинальный вопрос.
Леандро Глоссман
1

Да. Вот способ с использованием разделителя трубы (|),

try
{
    .......
}    
catch
{
    catch(IllegalArgumentException | SecurityException | IllegalAccessException | NoSuchFieldException e)
}
Шива
источник
Что это за стиль кода? Блок catch в блок try?
Сэм
1

Для kotlin это пока невозможно, но они решили добавить его: Source
Но пока, просто небольшая хитрость:

try {
    // code
} catch(ex:Exception) {
    when(ex) {
        is SomeException,
        is AnotherException -> {
            // handle
        }
        else -> throw ex
    }
}
Dr.jacky
источник
0

Поймать исключение, которое оказывается родительским классом в иерархии исключений. Это, конечно, плохая практика . В вашем случае общим родительским исключением является класс Exception, и перехват любого исключения, которое является экземпляром Exception, действительно является плохой практикой - исключения, такие как NullPointerException, обычно являются ошибками программирования и обычно должны решаться путем проверки нулевых значений.

Винит Рейнольдс
источник