Czy mogę złapać wiele wyjątków Java w tej samej klauzuli catch?


699

W Javie chcę zrobić coś takiego:

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

...zamiast:

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

Czy jest na to sposób?

Odpowiedzi:


1131

Jest to możliwe od wersji Java 7 . Składnia bloku wielozadaniowego jest następująca:

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

Pamiętaj jednak, że jeśli wszystkie wyjątki należą do tej samej hierarchii klas, możesz po prostu złapać ten podstawowy typ wyjątku.

Należy również pamiętać, że nie można wychwycić zarówno wyjątku A, jak i wyjątku B w tym samym bloku, jeśli wyjątek B jest dziedziczony, bezpośrednio lub pośrednio, od wyjątku A. Kompilator narzeka:

Alternatives in a multi-catch statement cannot be related by subclassing
  Alternative ExceptionB is a subclass of alternative ExceptionA

81
TT - dlaczego redefiniujemy operatora bitwise or( |)? Dlaczego nie użyć przecinka lub operatora, który ma bardziej podobne znaczenie, logical or( ||)?
ArtOfWarfare

11
@ArtOfWarfare Być może pomyśleli, że to już nie będzie miało znaczenia po tym, jak już wymyślili składnię wielu granic dla ogólnych.
JimmyB

12
Znak XOR (I) nie jest taki sam jak OR (||), A | B oznacza albo A albo B, ale nie oba A || B oznacza albo A albo B lub oba, więc dla wyjątków jest to albo wyjątek A, albo wyjątek B, ale nie oba jednocześnie. dlatego użyli śpiewu XOR zamiast OR i widać to wyraźnie, gdy wyjątek zostanie zgłoszony, jeśli
dodasz

41
@ user1512999 w Javie, bitowe XOR to ^ (daszek), a bitowe OR to | (pipe) docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
Lewis Baumstark

6
Warto wspomnieć, że rodzaj wyjątku wychwyconego w bloku z wieloma zaczepami jest oceniany do najbardziej pochodnego wspólnego
elementu

104

Nie dokładnie przed Javą 7, ale zrobiłbym coś takiego:

Java 6 i wcześniejsze

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();
}

11
Zauważ, że twój przykład w języku Java 6 przerywa zdolność kompilatora do określania, skąd zostanie wyrzucony.
MichaelBlume

2
@MichaelBlume True, co nie jest [takie] złe. Zawsze możesz uzyskać oryginalny wyjątek za pomocą exc.getCause(). Na marginesie, Robert C. Martin (między innymi) zaleca stosowanie niezaznaczonych wyjątków (kompilator nie ma pojęcia, jakiego rodzaju wyjątek zostanie z tego wyrzucony); zapoznaj się z rozdziałem 7: Obsługa błędów w jego książce Wyczyść kod .
user454322

4
W swoim przykładzie Java 6 nie powinieneś ponownie wprowadzać oryginalnego wyjątku zamiast tworzyć nową instancję wyjątku, tj. throw excZamiast throw new RuntimeException(exc)?
David DeMar

5
Jest to dość zła praktyka z punktu widzenia czytelności.
Rajesh J Advani,

3
Wystąpienie operacji jest nieco kosztowne, lepiej unikać jak najwięcej.
Paramesh Korrakuti

23

W Javie 7 możesz zdefiniować wiele klauzul catch, takich jak:

catch (IllegalArgumentException | SecurityException e)
{
    ...
}

16

Jeśli istnieje hierarchia wyjątków, można użyć klasy bazowej do przechwycenia wszystkich podklas wyjątków. W zdegenerowanym przypadku możesz przechwycić wszystkie wyjątki Java za pomocą:

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

W bardziej powszechnym przypadku, jeśli RepositoryException jest klasą bazową, a PathNotFoundException jest klasą pochodną, ​​to:

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

Powyższy kod przechwytuje wyjątek RepositoryException i PathNotFoundException dla jednego rodzaju obsługi wyjątków, a wszystkie pozostałe wyjątki są grupowane. Od wersji Java 7, zgodnie z odpowiedzią @ OscarRyz powyżej:

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

7
Klauzule BTW catch są obsługiwane w kolejności, więc jeśli umieścisz nadrzędną klasę wyjątków przed klasą potomną, to nigdy nie zostanie ona wywołana np .: try {...} catch (Exception e) {someCode (); } catch (RepositoryException re) {// nigdy nie osiągnięto}
Michael Shopsin

4
Właściwie właśnie dlatego, że nigdy nie można go osiągnąć, taki kod nawet się nie kompiluje.
polygenelubricants

15

Nie, jeden na klienta.

Możesz złapać nadklasę, taką jak java.lang.Exception, pod warunkiem, że we wszystkich przypadkach podejmiesz tę samą akcję.

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

Ale to może nie być najlepsza praktyka. Powinieneś wychwycić wyjątek tylko wtedy, gdy masz strategię faktycznego obchodzenia się z nim - a rejestrowanie i ponowne zgłaszanie nie oznacza „obsługi”. Jeśli nie masz działania naprawczego, lepiej dodaj go do podpisu metody i pozwól, aby pojawił się bąbelek dla kogoś, kto poradzi sobie z sytuacją.


20
Czy mogę złożyć petycję, aby sformułować fragment dotyczący złapania wyjątku java.lang.Exception? Zdaję sobie sprawę, że to przykład, ale wydaje mi się, że niektórzy ludzie mogą przeczytać tę odpowiedź i powiedzieć: „och, okej, po prostu złapię wtedy wyjątek”, kiedy prawdopodobnie nie tego chcą (lub powinni) zrobić.
Rob Hruska

2
Wiedziałem o tym, ale nie chcę tego robić ... No cóż, chyba utknąłem z 4 połowami, aż do następnej wersji Javy ...
froadie

@duffymo: Co jest złego w logowaniu i przepisywaniu? Tyle tylko, że zaśmieca kod, co jest równoznaczne z nie złapaniem go, prawda? Patrząc z ogólnej perspektywy strategii zarządzania błędami. Złe jest logowanie się, a nie ponowne wprowadzanie.
Frank Osterfeld,

5
Nie rozważam rejestrowania i ponownego przepisywania obsługi czegokolwiek. Wolałbym pozwolić, żeby to bańka trafiła do kogoś, kto może zrobić coś sensownego. Ta ostatnia warstwa, na której wyjątki nigdy nie powinny uciec (np. Kontrolery w aplikacji internetowej), powinna rejestrować błąd w tym przypadku.
duffymo

Czy jestem jedynym, który uważa za absurdalne, że dziennik nie jest dla mnie automatycznie generowany? Wydaje się, że wszyscy musimy napisać ten sam głupi komunikat logowania za każdym razem, gdy jakiś fragment kodu może zgłosić wyjątek.
ArtOfWarfare

10

Czystszą (ale mniej gadatliwą i być może nie tak preferowaną) alternatywą dla odpowiedzi user454322 na Javie 6 (tj. Na Androidzie) byłoby złapanie wszystkich Exceptions i powtórne rzutowanie RuntimeException. Nie działałoby to, jeśli planujesz wychwytywać inne typy wyjątków w dalszej części stosu (chyba że je również przerzucisz ponownie), ale skutecznie złapie wszystkie sprawdzone wyjątki.

Na przykład:

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
    } 
}

Biorąc to pod uwagę, dla szczegółowości najlepiej byłoby ustawić wartość logiczną lub inną zmienną i na podstawie tego wykonać jakiś kod po bloku try-catch.


1
Takie podejście uniemożliwia kompilatorowi określenie, czy „blok przechwytywania” będzie osiągalny.
the_new_mr

3

W wersji wcześniejszej niż 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
warto być dobrym rozwiązaniem. Co powiesz na wstawienie kontroli końcowej caughtw finallybloku?
Andrea_86

Wymaga to więcej wierszy niż oryginalne pytanie.
Leandro Glossman

1

Tak. Oto sposób użycia separatora pipe (|),

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

Co to za styl? Blok catch w blok try?
Sam

1

W przypadku kotlina nie jest to na razie możliwe, ale rozważali możliwość dodania go: Źródło.
Na razie tylko mała sztuczka:

try {
    // code
} catch(ex:Exception) {
    when(ex) {
        is SomeException,
        is AnotherException -> {
            // handle
        }
        else -> throw ex
    }
}

0

Złap wyjątek, który przypadkowo jest klasą nadrzędną w hierarchii wyjątków. To oczywiście zła praktyka . W twoim przypadku częstym wyjątkiem nadrzędnym jest klasa Exception, a wyłapanie dowolnego wyjątku, który jest wystąpieniem wyjątku, jest rzeczywiście złą praktyką - wyjątki takie jak NullPointerException są zwykle błędami programowymi i zwykle powinny zostać rozwiązane poprzez sprawdzenie wartości zerowych.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.