2015-07-23 10 views
21

Ok So che cattura throwable non è una buona idea:Catching Throwable e gestione delle eccezioni specifiche

try { 
     // Some code 
    } catch(Throwable e) { // Not cool! 
     // handle the exception 
    } 

Ma di recente ho letto attraverso un codice source aperto e ho visto questo (almeno a me) interessante pezzo di codice:

try { 
     // Some Code 
    } catch (Throwable ex){ 
     response = handleException(ex, resource); 
    } 

    private handleException(Throwable t, String resource) { 
     if (t instanceof SQLEXception) { 
       // Some code 
     } else if (t instanceof IllegalArgumentException) { 
       //some code 
     } //so on and so forth 
    } 

Questo non sembra essere così male? Cosa c'è di sbagliato in questo approccio?

+4

A volte, in particolare nei quadri, non avete altra scelta che prendere 'Throwable'. Non è ancora una buona idea nel codice a livello di applicazione, a meno che non si desideri avere una sorta di "gestore di ultima istanza". – biziclop

+0

Hai un link al codice open source in questione (come il file sorgente su GitHub)? Posso pensare a un paio di casi d'uso in cui, a seconda del contesto, questa potrebbe effettivamente essere una buona strategia. –

+0

@James_pic ci vai https://github.com/Eldelshell/jongo/blob/master/jongo-core/src/main/java/jongo/RestController.java –

risposta

17

Ci sono vari motivi per cui non dovresti prendere un Throwable. Prima di tutto, che Throwable include Error s - e normalmente non c'è molto che un'applicazione può fare se appare una di queste. Anche Throwable riduce le tue possibilità di scoprire, COSA è successo. Tutto quello che ottieni è "qualcosa di brutto è successo" - che potrebbe essere una catastrofe o solo una seccatura.

L'altro approccio è migliore, ma ovviamente non riuscirei a prendere Throwable, ma cerco di rilevare eccezioni più specifiche, se possibile. Altrimenti stai prendendo tutto e poi provi a risolvere il tipo di brutta cosa che è successo. Il tuo esempio potrebbe essere scritto come ...

try { 
    ... 
} catch (SQLEXception ex){ 
    response = ... ; 
} catch (IllegalArgumentException ex){ 
    response = ...; 
} 

... che ridurrebbe la quantità di if (... instanceof ...) blocchi (che sono necessari solo perché l'autore prima ha deciso di prendere tutto in un grosso secchio). È qualcosa in realtà throws Throwable, quindi non hai molta scelta, ovviamente.

+2

Il problema è che ci sono 'Error's che sono perfettamente facili da recuperare. Se un framework tenta di caricare una classe e fallisce, le cose potrebbero comunque andare bene. – biziclop

+0

In tal caso, il framework non dovrebbe produrre un errore, ma un'eccezione più significativa. Certo, non è sempre così, vero. –

+3

No, intendevo dire che il framework stesso deve intercettare l'errore e affrontarlo. Sfortunatamente gli autori di Java non hanno distinto tra errori recuperabili e non recuperabili, quindi a volte (molto raramente) catturare "Throwable" è l'unica opzione sensata. – biziclop

17

Hai ragione quando dici che catturare Throwable non è una buona idea. Tuttavia, il codice che presenti nella tua domanda non cattura lo Throwable in modo malvagio, ma ne parliamo più avanti. Per ora, il codice che si presenti nella sua domanda ha diversi vantaggi:

1. leggibilità

Se si guarda il codice con attenzione, si noterà che anche se il blocco catch sta recuperando un Throwable, il metodo handleException sta verificando il tipo di eccezione generata ed eventualmente prendendo diverse azioni in base al tipo di eccezione.

Il codice di cui la tua domanda è sinonimo a dire:

try { 
    doSomething(); 
} catch (SQLEXception ex){ 
    response = handleException(resource); 
} catch(IllegalArgumentException ex) { 
    response = handleException(resource); 
} catch(Throwable ex) { 
    response = handleException(resource); 
} 

Anche se si deve prendere 10 + eccezioni solo, questo codice può facilmente prendere un sacco di righe di codice e il multi-cattura la costruzione non renderà il codice più pulito. Il codice che presenti nella tua domanda è semplicemente delegare lo catch a un altro metodo per rendere più leggibile il metodo effettivo che fa il lavoro.

2. Riusabilità

Il codice per il metodo handleRequest potrebbe essere facilmente modificato e inserito in una classe di utilità e accessibile in tutta l'applicazione per gestire sia Exception s e Error s. È anche possibile estrarre il metodo in due metodi private; Uno che gestisce Exception e uno che gestisce Error e ha il metodo handleException che accetta un Throwable delegare ulteriormente le chiamate a questi metodi.

3.Maintainibility

Se si decide che si desidera cambiare il modo di registrare un SQLException s nella vostra applicazione, si deve fare questo cambiamento in un unico luogo, piuttosto che visitare ogni metodo in ogni classe che lancia un SQLException.

Quindi la cattura di Throwable è una cattiva idea?

Il codice che presenti nella tua domanda non è proprio lo stesso di quello che intercorre da solo Throwable. Il seguente pezzo di codice è un grande no-no:

try { 
    doSomething(); 
} catch(Throwable e) { 
    //log, rethrow or take some action 
} 

Si dovrebbe prendere Throwable o Exception più lontano nella catena catch possibile.

Ultimo ma non meno importante, ricorda che il codice che presenti nella tua domanda è il codice del framework e ci sono alcuni errori che il framework può ancora recuperare da. Vedere When to catch java.lang.Error per una spiegazione migliore.

+0

Perché dovresti usare 'Throwable' per quello e non solo 'Exception'? Il metodo dovrebbe essere chiamato handleThrowable ... – maraca

+2

@maraca Ricorda che questo è il codice framework. Fameworks vorrebbe rilevare errori recuperabili. (Gli errori di collegamento sono un ottimo esempio). Detto questo, sono d'accordo che il nome del metodo che hanno usato non è davvero una buona scelta qui, ma non è legato alla domanda reale. – CKing

+0

Grazie vedo. Ma penso che sia un problema di design, non è possibile trovare uno scenario in cui questo sarebbe un buon approccio. Invece di prendere tutto e chiamare questo metodo, probabilmente dovrebbero usare solo tiri e gestire nel posto giusto. – maraca

9

Cattura Throwable s per pigrizia è una cattiva idea.

Questo era particolarmente allettante prima che fosse introdotto try-multi-catch.

try { 
    ... 
} catch (SomeException e) { 
    //do something 
} catch (OtherException e) { 
    //do the same thing 
} ... 

Ripetizione blocchi catch è noioso e prolisso, per cui alcune persone hanno deciso di prendere solo Exception o Throwable e da fare con esso. Questo è ciò che dovrebbe essere evitato perché:

  1. Rende difficile seguire ciò che si sta tentando di fare.
  2. Si può finire per prendere un sacco di cose che non si possono affrontare.
  3. Ti meriti una punizione bonus se ingerisci completamente il Throwable nel blocco catch. (E abbiamo visto tutti il ​​codice che lo fa ... :))

Ma recuperando Throwable s quando è assolutamente necessario va bene.

Quando è necessario? Molto raramente. Nel codice di tipo framework ci sono vari scenari (il caricamento dinamico di una classe esterna è il più ovvio), in un'applicazione standalone un tipico esempio è il tentativo di visualizzare/registrare una sorta di messaggio di errore prima di uscire. (Tenendo presente che il tentativo potrebbe fallire, quindi non si vuole mettere nulla di critico lì.)

Come regola generale, se non c'è nulla che si possa fare su un'eccezione/errore, non si dovrebbe prendilo per niente

2

Prima di tutto, la cattura di Throwable rende la tua applicazione piuttosto intransigente. Dovresti essere il più esplicito possibile nel rilevare le eccezioni per consentire una buona tracciabilità in casi eccezionali.

Diamo uno sguardo al metodo di HandleException (...) e vedere alcuni dei problemi che si verificano da questo approccio:

  • si cattura Throwable, ma si occupano solo delle eccezioni, che cosa succede se un esempioOutOfMemoryError di tipo Error viene generato? - Vedo che accadono cose brutte ...
  • Per quanto riguarda la buona programmazione orientata agli oggetti usando instanceof si interrompe il principio Open-Closed e si fanno veramente delle modifiche al codice (ad esempio aggiungendo nuove eccezioni).

Dal mio punto di vista, i blocchi di cattura sono fatti esattamente per la funzionalità che si tenta di coprire in handleExceptions (...), quindi usarli.

2

Java 7 risolve un po 'la noia che è multi-catching di simili eccezioni con gestione simile. Sicuramente non dovresti fare quello che la persona ha fatto qui. Basta prendere le opportune eccezioni, se necessario, può sembrare brutto, ma questo è ciò che è throws, passarlo al metodo che dovrebbe catturarlo e non si dovrebbe sprecare troppo spazio nel codice.

Check out this link for more information.

2

solo per fornire equilibrio - c'è un luogo in cui mi sarà sempre catch (Throwable):

public static void main(String args[]) { 
    try { 
     new Test().test(); 
    } catch (Throwable t) { 
     t.printStackTrace(System.err); 
    } 
} 

almeno qualcosa mostra da qualche parte che qualcosa è andato storto.

+0

Davvero ??? Non lo faccio mai ... come sempre viene stampato, comunque. Forse è specifico per il sistema operativo? – maaartinus

3

È stato pubblicato un collegamento a Jongo, che dimostra un possibile utilizzo per questa tecnica: riutilizzo del codice di gestione degli errori.

Diciamo che hai un grande blocco di codice di gestione degli errori che si ripete in modo naturale in vari punti del codice, ad esempio Jongo produce risposte standard per alcune classi standard di errori. Potrebbe essere una buona idea estrarre il codice di gestione degli errori in un metodo, in modo da poterlo riutilizzare da tutti i punti in cui è necessario.

Tuttavia, questo non vuol dire che non c'è nulla di sbagliato nel codice di Jongo.

Facendo Throwable (invece di usare multicatch) è ancora sospettoso, come è molto probabile che la cattura Error s che non sei davvero in grado di gestire (sei sicuri si intende per catturare ThreadDeath?). In questa situazione, se devi assolutamente catturare lo Throwable, sarebbe meglio "prendere e rilasciare" (cioè, rilanciare tutto ciò che non volevi prendere). Jongo non lo fa.

3

Ci sono esattamente due utilizzi validi per l'utilizzo di una rete enorme:

  • Se si occuperà di tutto in modo uniforme, come un fermo di livello superiore per la registrazione/reporting, eventualmente seguita da un'uscita immediata.

  • Per ridurre la duplicazione, esportando tutta la gestione nel proprio metodo.
    Cattura l'antenato comune più derivato per evitare lavori extra e aumentare la chiarezza.
    DRY è un importante principio di progettazione.

In entrambi i casi, a meno che non ci si aspettasse quell'eccezione e la si gestisse completamente, si ripete.

0

È sempre possibile rilevare diversi tipi di eccezioni ed eseguire alcune operazioni in base al tipo di eccezione che si ottiene.

Ecco un esempio

  try{ 

       //do something that could throw an exception 

      }catch (ConnectException e) { 
       //do something related to connection 


      } catch (InvalidAttributeValueException e) { 
       // do anything related to invalid attribute exception 

      } catch (NullPointerException e) { 

       // do something if a null if obtained 
      } 

      catch (Exception e) { 
      // any other exception that is not handled can be catch here, handle it here 

      } 
      finally{ 
      //perform the final operatin like closing the connections etc. 
      }