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?

froadie
źródło

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
OscarRyz
źródło
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();
}
użytkownik454322
źródło
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)
{
    ...
}
crusam
źródło
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 ) { 
  ...
}
Michael Shopsin
źródło
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ą.

duffymo
źródło
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.

Oleg Waszkiewicz
źródło
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.
  }
Rachunki
źródło
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)
}
siedmiodniowa żałoba
źródło
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
    }
}
Dr.jacky
źródło
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.

Vineet Reynolds
źródło