2009-08-03 8 views
16

Qual è la procedura migliore da seguire quando è necessario generare un'eccezione non definita in un'interfaccia che si sta implementando?Lancio di un'eccezione non definita nell'interfaccia

Ecco un esempio:

public interface Reader 
{ 
    public abstract void read() throws IOException; 
} 

public class CarrotReader implements Reader 
{ 
    public void read() throws IOException {} 
} 

public class CupcakeReader implements Reader 
{ 
    public void read() throws IOException, CupcakeException {} 
} 

In questo caso, si dispone di una specifica deroga che si verifica quando la lettura di dolcetti, così si vuole lanciare un'eccezione relative a questo. Tuttavia, Reader non definisce questo tipo di eccezione nella sua interfaccia, quindi cosa fai? Inoltre, non ha senso aggiungere CupcakeException al tiri clausola del Reader interfaccia , perché questo tipo di eccezione è specifico per CupcakeReader. Un modo per aggirare questo è quello di definire leggere in modo tale che genera un tipo genitore, come Eccezione, ma poi si perde il contesto per l'eccezione. Cosa dovresti fare in questa situazione? Grazie!


Un'altra situazione interessante che è stata sollevata comporta un'interfaccia su cui non si ha alcun controllo. In questo caso, qual è il modo migliore per indicare che si è verificato un problema?

A scopo illustrativo, qui è un altro esempio:

public interface Reader 
{ 
    public abstract void read(); 
} 

public class CupcakeReader implements Reader 
{ 
    public void read() throws CupcakeException {} 
} 

In questo caso, non è possibile modificare Reader, ma si vuole indicare che un problema si è verificato in CupcakeReader s' leggere metodo.

+0

Questo è ancora peggio di quello che pensavo. In questo modo non puoi nemmeno usare il concatenamento per eludere il sistema. Penso che tu debba ricorrere a (ahi!) 'RuntimeExceptions'. –

+0

Ho fatto ricorso a System.err.println e a exception.printStackTrace() ;. È appropriato o dovrei propagare l'errore con un RuntimeExecption come hai detto? – Scott

+3

Penso che log + swallow non sia appropriato al 99,99% delle volte. È un modello comune (anti -?) Tra gli sviluppatori Java a causa del problema presentato qui. Nella mia "scuola di pensiero", ci sono solo tre modi validi per gestire le eccezioni: rilanciare, gestire (ad esempio, risolvere il problema), o lasciarlo scoppiare a qualcuno che può gestirlo. Non ingoiare mai. Se qualcosa è andato storto, ci saranno conseguenze. –

risposta

12

Potrebbe essere necessario creare un'eccezione del tipo previsto.

... catch(CupcakeException e) { 
    throw new IOException("The sky is falling", e); 
} 
+1

+1 per menzionare il concatenamento di eccezioni. –

+0

Questo è quello che sto facendo al momento. – Scott

+0

Questa è probabilmente la soluzione migliore se non è possibile modificare l'interfaccia. Puoi comunque accedere a tutte le informazioni di cui potresti avere bisogno e segui comunque il contratto dell'interfaccia. –

0

Forse potresti creare una classe ReaderSpecificException astratta, metterla nell'interfaccia e la sottoclasse CupcakeException da questa classe astratta.

+1

Non penso sia una buona pratica creare un'eccezione come una classe astratta. Per impostazione predefinita, un'eccezione senza attributi in essa può comunque fornire informazioni sul problema (semplicemente scegliendo il nome giusto). Perché dovresti definirlo come una classe astratta? – Manglu

9

Utilizzare qualcosa chiamato ReaderException che fungerà da interfaccia principale della gerarchia di eccezioni. ReaderException fornisce anche un collegamento ad altre eccezioni che vengono generate a causa di eccezioni di livello inferiore.

+0

Non è la stessa cosa che aggiungere la clausola Exceptions alla clausola throws? Mi rendo conto che definendo la propria classe genitore che è in qualche modo più specifica, si conserva più del contesto, ma a un certo livello si stanno ancora perdendo informazioni, giusto? – Scott

+0

Questo è quello che faccio di solito (+1). Ma cosa faresti se non fosse possibile cambiare l'interfaccia (il codice non è tuo, il codice è già stato spedito, ecc.)? –

+0

@Scott: non si perde nulla. Se lanci un 'CupcakeException' che estende' ReaderException', puoi prenderlo come 'ReaderException' o come' CupcakeException', a seconda di quanto specifico vuoi essere. –

0

Se si crea una maggiore un'eccezione astratto che funziona come una classe base per CupCakeException non si associa l'interfaccia del lettore ad una specifica applicazione, come si sarebbe fare se si è aggiunto il CupCakeException all'interfaccia Reader. Se non si eredita un'eccezione da un altro, c'è un constructor nella classe di eccezioni che accetta un throwable come secondo argomento come Thorbjørn Ravn Andersen ha già mostrato nel suo esempio di codice breve. Ti consente di generare un'eccezione più astratta e ogni parte del tuo codice che deve sapere di più, solo che "c'è un errore" può cercare la causa dell'eccezione più alta.

1

Basta non utilizzare le eccezioni controllate.

L'esempio che hai mostrato è uno dei motivi per cui le eccezioni controllate sono sbagliate.

Il motivo principale è che l'utente del proprio lettore di cupcake dovrà gestire la propria eccezione indipendentemente dal fatto che sia interessato o no.

Così, invece di:

Value value = reader.read(); 

Lo costringono a fare questo:

Value value = null; 
try { 
    value = reader.read(); 
} catch (Exception e) { 
    // now what?? 
} 

value.doSomething(); // potential NPE here 

Pensate che uno è migliore, più leggibile e meno soggetta ad errori e solo smettere di usare eccezioni controllate.

EDIT:

Sono sorpreso con il rating negativo. Ci sono persone che pensano ancora che le eccezioni controllate siano grandi? Se è così ecco alcuni riferimenti per cui non si dovrebbe usare eccezioni controllate:

eccezioni
  • No quadro moderno utilizza controllato (primavera, EJB3 ecc)
  • articolo con esempi di codice here
  • StackOverflow topic
  • Efficace Java (sezioni 58 e 59) - here
+4

Penso che stai postando un esempio deliberatamente fuorviante. Scriverò quanto sopra per dichiarare e utilizzare la variabile all'interno del blocco try {}. Non c'è motivo per cui dovresti scrivere codice come sopra (indipendentemente dal fatto che le eccezioni siano controllate o meno) –

+5

Il problema con le eccezioni controllate non è quello presentato qui. Come ha sottolineato Brian, questo codice è semplicemente sbagliato. Un problema con le eccezioni controllate è che si perde qualsiasi vantaggio nel fare controlli preventivi (quando è possibile) prima di chiamare il codice che genera eccezioni. Anche se fai controlli preventivi, sei costretto a usare un blocco try-catch. Un altro è il fatto che se si ha qualcosa in cui il metodo N chiama deeps e si chiama 0 può gestirlo, ogni chiamata da 0 a N deve includere tiri nel contratto (o imbrogliare con 'RuntimeException's –

+0

Leggi questo per ulteriori informazioni sul "perché no" delle eccezioni controllate: http://www.artima.com/intv/handcuffs.html –

2

L'eccezione è parte dell'interfaccia. Definire un genitore generico per tutte le eccezioni nell'interfaccia se è possibile ridefinire l'interfaccia.

È inoltre possibile rendere CupcakeException un figlio di IOException.

+0

Avere un "voto in su" per "anche". Ma sono di parte, perché è quello che avrei probabilmente fatto (sottoclasse dell'eccezione dichiarata, se necessario). Naturalmente, avendo una sottoclasse di (ad esempio) IOException presuppone che il chiamante debba effettivamente preoccuparsi che qualcosa di più specifico sia accaduto, oltre a implicare che il problema sia davvero una sorta di problema I/O. – Roboprog