Consideriamo la situazione in cui hai il codice fornito di:
public void delete() throws IOException, SQLException { // Non-Compliant
/* ... */
}
Il pericolo qui è che il codice che scrivi per chiamare delete()
sarà simile a:
try {
foo.delete()
} catch (Exception e) {
/* ... */
}
Anche questo è male. E verrà catturato con un'altra regola che i flag catturano la classe Exception di base.
La chiave è non scrivere codice che ti fa venir voglia di scrivere codice cattivo altrove.
La regola che stai incontrando è piuttosto comune. Checkstyle ha le sue regole di progettazione:
ThrowsCount
Restricts throws statements to a specified count (1 by default).
Rationale: Exceptions form part of a method's interface. Declaring a method to throw too many differently rooted exceptions makes exception handling onerous and leads to poor programming practices such as writing code like catch(Exception ex). This check forces developers to put exceptions into a hierarchy such that in the simplest case, only one type of exception need be checked for by a caller but any subclasses can be caught specifically if necessary.
Questo descrive esattamente il problema e qual è il problema e perché non dovresti farlo. È uno standard ben accettato che molti strumenti di analisi statica identificheranno e contrassegneranno.
E mentre potresti farlo secondo il design del linguaggio, e potresti essere volte in cui è la cosa giusta da fare, è qualcosa che dovresti vedere e vai subito "ehm, perché sto facendo questo?" Può essere accettabile per il codice interno in cui tutti sono disciplinati abbastanza da non catch (Exception e) {}
, ma il più delle volte ho visto le persone tagliare le curve soprattutto in situazioni interne.
Non fare in modo che le persone che usano la tua classe vogliano scrivere codice errato.
Devo sottolineare che l'importanza di ciò è ridotta con Java SE 7 e successivi perché una singola istruzione catch può catturare più eccezioni (Catching Multiple Exception Types e Rethrowing Exceptions con Migliorato il controllo del tipo da Oracle).
Con Java 6 e precedenti, avresti un codice simile a:
public void delete() throws IOException, SQLException {
/* ... */
}
e
try {
foo.delete()
} catch (IOException ex) {
logger.log(ex);
throw ex;
} catch (SQLException ex) {
logger.log(ex);
throw ex;
}
o
try {
foo.delete()
} catch (Exception ex) {
logger.log(ex);
throw ex;
}
Nessuna di queste opzioni con Java 6 è l'ideale. Il primo approccio viola DRY . Blocchi multipli che fanno la stessa cosa, ancora e ancora - una volta per ogni eccezione. Vuoi registrare l'eccezione e ricrearla? Ok. Le stesse linee di codice per ogni eccezione.
La seconda opzione è peggiore per diversi motivi. Primo, significa che stai prendendo tutte le eccezioni. Puntatore nullo viene catturato lì (e non dovrebbe). Inoltre, stai rilanciando un Exception
, il che significa che la firma del metodo sarebbe deleteSomething() throws Exception
, il che rende ancora più complicato lo stack poiché le persone che usano il tuo codice ora sono forzate a catch(Exception e)
.
Con Java 7, questo non è come importante perché puoi invece fare:
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
Inoltre, il tipo controlla se uno fa cattura i tipi di eccezioni lanciate:
public void rethrowException(String exceptionName)
throws IOException, SQLException {
try {
foo.delete();
} catch (Exception e) {
throw e;
}
}
Il type checker riconoscerà che e
potrebbe solo essere di tipi IOException
o SQLException
. Non sono ancora eccessivamente entusiasta dell'uso di questo stile, ma non sta causando un codice errato come lo era in Java 6 (dove ti costringerebbe ad avere la firma del metodo come la superclasse che le eccezioni estendono).
Nonostante tutti questi cambiamenti, molti strumenti di analisi statica (Sonar, PMD, Checkstyle) stanno ancora applicando le guide in stile Java 6. Non è una brutta cosa Tendo ad essere d'accordo con un avvertimento per far sì che ancora venga applicato, ma potresti cambiare la priorità su di loro in maggiore o in minore in base al modo in cui il team li assegna per priorità.
Se le eccezioni devono essere selezionate o deselezionate ... si tratta di g r e a t dibattito che si può facilmente trovare innumerevoli post sul blog che occupano entrambi i lati dell'argomento. Tuttavia, se stai lavorando con eccezioni controllate, probabilmente dovresti evitare di lanciare più tipi, almeno in Java 6.