generare un'eccezione di runtime nell'applicazione Java


12

Sto lavorando come appaltatore alla progettazione di applicazioni Java aziendali per il mio cliente nel ruolo di responsabile tecnico. L'applicazione verrà utilizzata dagli utenti finali e ci sarà un team di supporto che supporterà l'applicazione al momento della partenza.

Gli altri lead tecnici con cui sto lavorando hanno l'impressione che la gestione delle eccezioni renderà sporco il codice. Il sistema dovrebbe generare eccezioni verificate solo dal livello Servizio e il resto del codice dovrebbe generare eccezioni di runtime da tutti gli altri livelli in modo che non sia necessario gestire le eccezioni non controllate.

Qual è la necessità di lanciare un'eccezione non selezionata in un'applicazione aziendale?

Dalla mia esperienza in passato sulle eccezioni di runtime:

1) Le eccezioni non selezionate rendono il codice imprevedibile perché non vengono visualizzate nemmeno in Javadoc.

2) Generare eccezioni non selezionate in un'applicazione aziendale non ha senso perché quando lo si lancia e va dritto in faccia agli Utenti, come si spiega all'utente? Ho visto abbastanza applicazioni web che mostrano 500 -Internal Error. Contact Administratorche non significa nulla per un utente finale o per il team di supporto che gestisce l'applicazione.

3) Il lancio di eccezioni di runtime costringe gli utenti della classe che lanciano l'eccezione a eseguire il debug del codice sorgente per verificare il motivo per cui viene generata l'eccezione. Questo può essere evitato solo se il Javadoc dell'eccezione di runtime risulta essere documentato in modo eccellente, che trovo non sia mai un caso.


Puoi spiegare cos'è il livello di servizio? Il livello è più lontano dall'utente o più vicino all'utente?
Manoj R,

Perché questa domanda non si adatta a SO?
Bjarke Freund-Hansen,

@Manoj R: Nemmeno. Un livello o livello di servizio è dove vengono rispettate le regole e la logica di business, separate dai dettagli della tecnologia DB e dalla presentazione del client.
Michael Borgwardt,

6
Le eccezioni non selezionate [...] non vengono visualizzate nemmeno nel Javadoc. => appariranno se li documenterai con il @throwstag, che tra l'altro è una buona pratica.
Assylias,

4
@SureshKoya Effective Java, Item 62: Documenta tutte le eccezioni generate da ciascun metodo. Estrai: utilizzare il @throwstag Javadoc per documentare ogni eccezione non selezionata che può essere generata da un metodo, ma non utilizzare la parola chiave throw per includere le eccezioni non selezionate nella dichiarazione del metodo.
Assylias,

Risposte:


13

Le eccezioni verificate sono un'esperimento fallito nella progettazione del linguaggio. Ti costringono ad avere astrazioni estremamente perse e codice sporco. Dovrebbero essere evitati il ​​più possibile.

Per quanto riguarda i tuoi punti:

1) Le eccezioni controllate rendono il codice sporco e non meno imprevedibile perché si presentano ovunque .

2) In che modo viene mostrata all'utente un'eccezione controllata? L'unica vera differenza tra le eccezioni verificate e non selezionate è tecnica e riguarda solo il codice sorgente.

3) Hai mai sentito parlare delle tracce dello stack? Ti dicono esattamente dove è stata generata l'eccezione, indipendentemente dal fatto che sia selezionata o deselezionata. In realtà, le eccezioni controllate tendono ad essere peggiori per il debug perché sono spesso racchiuse, il che porta a tracce di stack più lunghe e più brutte, o addirittura perse del tutto perché il wrapping è stato fatto in modo sbagliato.

Esistono due tipi di eccezioni: quelle che si verificano "normalmente" e sono in genere gestite molto vicino al luogo in cui si verificano e quelle che sono davvero eccezionali e possono essere gestite genericamente in un livello molto alto (basta interrompere l'azione corrente e registrarla / mostra un errore).

Le eccezioni verificate sono state un tentativo di inserire questa distinzione nella sintassi del linguaggio nel punto in cui sono state definite le eccezioni. I problemi con questo sono

  • La distinzione dipende davvero dal chiamante , non dal codice che genera l'eccezione
  • È completamente ortogonale al significato semantico di un'eccezione, ma legarlo alla gerarchia di classi ti costringe a mescolare i due
  • L'intero punto delle eccezioni è che puoi decidere a quale livello catturarli senza rischiare di perdere un errore silenziosamente o di dover inquinare il codice a livelli intermedi oppure; le eccezioni verificate perdono quel secondo vantaggio.

1. Bene, nel caso di un'eccezione non selezionata, non saprai nemmeno se una chiamata potrebbe risultare in una non selezionata. 2. Quando hai detto "È completamente ortogonale al significato semantico di un'eccezione, ma legarlo alla gerarchia di classi ti costringe a mescolare i due", suppongo che intendevi chiamare gerarchia anziché gerarchia di classe.
randominstanceOfLivingThing

2
@Suresh Koya: No, intendevo gerarchia di classi, il fatto che una dichiarazione SQLException extends Exceptionsignifichi che la scelta di lanciare un SQLExceptionperché un errore ha a che fare con l'accesso al DB significa automaticamente che l'eccezione è controllata. E puoi documentare bene le eccezioni non selezionate nei commenti Javadoc. Funziona bene per tutte le altre lingue.
Michael Borgwardt,

1
@MichaelBorgwardt "Le eccezioni verificate sono un'esperimento fallito nella progettazione del linguaggio", è la tua opinione personale se non mi piacerebbe saperne di più. Qualsiasi link autentico sarebbe di grande aiuto.
Vipin,

2
@Vipin: è la mia opinione personale, ma condivisa da molte altre persone, ad esempio Bruce Eckel: mindview.net/Etc/Discussions/CheckedExceptions - e il fatto che nessun'altra lingua abbia adottato eccezioni verificate nei 20 anni dall'introduzione di Java parla per se stesso ...
Michael Borgwardt,

2

Il mio punto di vista è che il tipo di eccezione generata dipende da cosa sta facendo il tuo codice.

Lancio eccezioni verificate quando mi aspetto che possano accadere abbastanza spesso (ad esempio utenti che immettono dati non validi) e mi aspetto che il codice chiamante gestisca la situazione.

Lancio eccezioni non controllate / di runtime (non spesso come selezionate) quando ho una situazione rara che non mi aspetto che il codice chiamante gestisca. Un esempio potrebbe essere una sorta di strano errore relativo alla memoria che non mi aspetto che si verifichi. Non spuntati sono i tipi di errori che ti aspetti di far cadere l'applicazione.

Nessuna eccezione dovrebbe apparire di fronte a un utente senza un certo livello di supporto. Anche se è solo un "tagliare e incollare questo dump dell'errore in un'e-mail". Niente è più fastidioso per un utente di quanto sia stato detto che c'è un errore, ma non viene fornito alcun dettaglio o azione che possono intraprendere per avviarlo.

La mia ipotesi è che la filosofia che menzioni provenga da una di due fonti:

  • Programmatori pigri che cercano di evitare di lavorare.
  • O gli sviluppatori che hanno dovuto supportare il codice che è andato dall'altra parte. cioè gestione di errori eccessivi. Il tipo di codice che contiene grandi quantità di gestione delle eccezioni, molte delle quali non fanno nulla, o anche peggio, viene utilizzato per il controllo del flusso e altri scopi errati.

Se qualcosa può succedere abbastanza spesso, non fa eccezione. Ma ha convenuto che l'errore non dovrebbe essere lanciato, di cui l'utente non ha idea.
Manoj R,

Sono d'accordo sulla tua filosofia. Su un'applicazione che stai sviluppando per gli utenti, quale sarà la necessità di generare un'eccezione di runtime?
randominstanceOfLivingThing

Inoltre, anche se viene generata un'eccezione di runtime, preferisco la convenzione indicata da @assylias.
randominstanceOfLivingThing

1

Se non si desidera eccezioni di runtime non selezionate, perché si utilizza Java? Esiste la possibilità di eccezioni di runtime quasi ovunque - in particolare NullPointerException, ArithmeticException, ArrayIndexOutOfBounds, ecc.

E una volta che leggi i file di registro di alcuni sistemi J2EE, ad esempio un'installazione di SAP NetWeaver, vedrai che tali eccezioni si verificano letteralmente tutto il tempo.


Dalla specifica del linguaggio Java: "Le classi di eccezioni di runtime (RuntimeException e le sue sottoclassi) sono esentate dal controllo del tempo di compilazione perché, a giudizio dei progettisti del linguaggio di programmazione Java, dover dichiarare tali eccezioni non aiuterebbe in modo significativo a stabilire la correttezza di programmi. Molte operazioni e costrutti del linguaggio di programmazione Java possono comportare eccezioni di runtime. "
randominstanceOfLivingThing

1
L'eccezione di runtime di cui stai parlando sono quelli che derivano da Java poiché il sistema di runtime Java non ha la minima idea del motivo per cui stai provando a fare la chiamata specifica. Ad esempio: sorgerebbe una NullPointerException se si chiama un metodo che è null. In tal caso, il sistema di runtime Java non ha una parola giusta per la follia dei programmatori e darebbe uno schiaffo al chiamante con una NullPointerException. La parte triste è che il chiamante ti ha venduto il prodotto Netweaver e tu come utente ora sei vittima di una programmazione scadente. I tester hanno la stessa colpa poiché non hanno fatto tutti i test di confine.
randominstanceOfLivingThing

1

Ci sono alcune regole per la gestione delle eccezioni che dovresti tenere a mente. Ma prima, devi ricordare che le eccezioni fanno parte dell'interfaccia esposta dal codice; documentarli . Ciò è particolarmente importante quando l'interfaccia è pubblica, ovviamente, ma è un'ottima idea anche nelle interfacce private.

Le eccezioni dovrebbero essere gestite solo nel punto in cui il codice può fare qualcosa di ragionevole con loro. L'opzione di gestione peggiore è di non fare nulla al riguardo, cosa che dovrebbe essere fatta solo quando questa è esattamente l'opzione corretta. (Quando ho una situazione del genere nel mio codice, includo un commento in tal senso, quindi so di non preoccuparmi del corpo vuoto.)

La seconda opzione peggiore è lanciare un'eccezione non correlata senza l'originale allegato come causa. Il problema qui è che le informazioni all'interno dell'eccezione originale che consentirebbero la diagnosi del problema vanno perse; stai creando qualcosa con cui nessuno può fare nulla (oltre a lamentarsi del fatto che "non funziona" e sappiamo tutti come odiamo le segnalazioni di bug).

Molto meglio è la registrazione dell'eccezione. Ciò consente a qualcuno di scoprire qual è il problema e risolverlo, ma è necessario registrare l'eccezione solo nel punto in cui verrebbe altrimenti perso o segnalato su una connessione esterna. Ciò non è dovuto al fatto che la registrazione più frequente è un grave problema in quanto tale, ma piuttosto perché una registrazione eccessiva significa che il registro consuma solo più spazio senza contenere più informazioni. Una volta effettuato l'accesso l'eccezione, è possibile segnalare un précis per l'utente / cliente con una buona coscienza (a patto che si includa anche l'ora di creazione - o altro identificativo di correlazione - in tale relazione in modo che la versione corta può essere abbinato con i dettagli se necessario).

L'opzione migliore è, ovviamente, gestire completamente l'eccezione, affrontando la situazione di errore nella sua interezza. Se riesci a farlo, fallo sicuramente! Potrebbe anche significare che è possibile evitare di registrare l'eccezione.

Un modo per gestire un'eccezione è di lanciare un'altra eccezione che fornisce una descrizione di livello superiore del problema (ad esempio, " failed to initialize" anziché " index out of bounds"). Questo è un buon modello fino a quando non perdi le informazioni sulla causa dell'eccezione; utilizzare l'eccezione dettagliata per inizializzare l' causeeccezione di livello superiore o registrare i dettagli (come discusso sopra). La registrazione è più appropriata quando si sta per attraversare un confine tra processi, come una chiamata IPC, perché non esiste alcuna garanzia che la classe di eccezione di basso livello sarà presente all'altra estremità della connessione. Conservare come causa associata è più appropriato quando si attraversa un confine interno.

Un altro modello che vedi è catch-and-release:

try {
    // ...
} catch (FooException e) {
    throw e;
}

Questo è un anti-pattern a meno che tu non abbia vincoli di tipo da altre catchclausole che significano che non puoi lasciare che l'eccezione passi da sola. Quindi è solo una brutta caratteristica di Java.

Non vi è alcuna reale differenza tra le eccezioni verificate e quelle non selezionate oltre al fatto che è necessario dichiarare eccezioni controllate che attraversano i limiti del metodo. È comunque una buona idea documentare le eccezioni non controllate (con il @throwscommento javadoc) se sai che sono state lanciate deliberatamente dal tuo codice. Non gettare deliberatamente java.lang.Erroro le sue sottoclassi (a meno che non si stia scrivendo un'implementazione JVM).

Opinione: Un caso di errore imprevisto rappresenta sempre un bug nel codice. Le eccezioni verificate sono un modo per gestire questa minaccia e dove gli sviluppatori utilizzano deliberatamente eccezioni non controllate come modo per evitare il problema di gestire i casi di errore, si stanno accumulando molti debiti tecnici che si dovranno ripulire un po 'di tempo se vuoi un codice robusto. La gestione degli errori sciatta non è professionale (e guardare la gestione degli errori è un buon modo per determinare quanto sia realmente bravo un programmatore).


0

Penso che dovresti lanciare SOLO un'eccezione controllata quando l'applicazione può recuperare. Quindi, gli utenti della tua libreria devono catturare quelle eccezioni e fare ciò di cui hanno bisogno per recuperare. Qualsiasi altra cosa dovrebbe essere deselezionata.

Ad esempio,

Se l'app carica i dati da un file o da un database. Poi,..

try {
  File data = new File(...);
  // parse file here
} catch (Exception ex) {
  throw new MissingDataFileException("data file not found");
}

il chiamante può sempre catturare MissingDataFileException selezionata e quindi provare a caricare i dati dal database.

try {
  Connection con = DriverManager.getConnection( host, username, password );
  // query data here
} catch (Exception ex) {
  throw new RuntimeException("better call Saul!");
}

1
Saul è morto in un incidente d'auto 3 anni fa. Fallire! ;-)
Donal Fellows
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.