2010-02-05 4 views
25

L'interfaccia java.lang.Iterator ha 3 metodi: hasNext, next e remove. Per implementare un iteratore di sola lettura, è necessario fornire un'implementazione per 2 di questi: hasNext e next.Cosa fare delle eccezioni durante l'implementazione di java.lang.Iterator

Il mio problema è che questi metodi non dichiarano eccezioni. Quindi, se il mio codice all'interno del processo di iterazione dichiara eccezioni, devo racchiudere il mio codice di iterazione all'interno di un blocco try/catch.

La mia politica attuale è stata quella di ripensare l'eccezione racchiusa in un RuntimeException. Ma questo ha problemi perché le eccezioni controllate sono perse e il codice client non è più in grado di catturare esplicitamente tali eccezioni.

Come posso aggirare questa limitazione nella classe Iterator?

Ecco un codice di esempio per la chiarezza:

class MyIterator implements Iterator 
{ 
    @Override 
    public boolean hasNext() 
    { 
     try 
     { 
      return implementation.testForNext(); 
     } 
     catch (SomethingBadException e) 
     { 
      throw new RuntimeException(e); 
     } 
    } 

    @Override 
    public boolean next() 
    { 
     try 
     { 
      return implementation.getNext(); 
     } 

     catch (SomethingBadException e) 
     { 
      throw new RuntimeException(e); 
     } 
    } 

    ... 
} 

risposta

9

Ho implementato un sacco di Iterator, a volte sopra iteratori with-check-exception (ResultSet è concettualmente un iteratore di record, InputStream y concettualmente un iteratore di byte) e così via. È molto, molto bello e maneggevole (puoi implementare l'architettura dei filtri & per un sacco di cose).

Se si preferisce dichiarare le proprie eccezioni, quindi dichiarare un nuovo tipo di Iterator (ExceptionIterator, sarebbe come Runnable e Callable). Puoi usarlo insieme o il tuo codice ma non puoi comporlo con componenti esterni (Java Class Library o 3d party libs).

Ma se si preferisce utilizzare le interfacce super-standard (come iteratore) per utilizzarle ovunque, quindi utilizzare Iterator. Se sai che le tue Eccezioni saranno una condizione per interrompere l'elaborazione, o non ti dispiace molto ... usale.

Le eccezioni di runtime non sono così terribili. Per esempio. Hibernate li usa per implementare proxy e cose del genere. Devono eccezioni tranne DB, ma non possono dichiararle nelle loro implementazioni di List.

+1

Un'altra opzione, se non è possibile controllare il codice client UI: creare un iteratore che registrare le eccezioni da qualche parte accessibile con un getter o qualcosa, o chiamare un interfaccia di callback, o qualcosa di controllare e possibile visualizzare l'errore da nessuna parte. Se ciò ti è utile, puoi creare un "CatcherIterator" che semplicemente racchiude un runtime-exception-thrower-iterator e cattura tutte le eccezioni possibili reindirizzandole alla proprietà o all'interfaccia di callback. – helios

1

Se l'implementazione non ha le proprietà richieste dall'interfaccia Iterator, perché si desidera utilizzarlo?

Non vedo altra soluzione rispetto a RuntimeException (con i suoi problemi).

11

Si dovrebbe rilanciare l'eccezione come eccezione di runtime personalizzata, non generica, ad esempio SomethingBadRuntimeException. Inoltre, puoi provare exception tunneling.

E sono certo che forzare il cliente a gestire le eccezioni facendole controllare è una cattiva pratica. Semplicemente inquina il tuo codice o le tue forze per avvolgere le eccezioni con quelle di runtime o forzarle a processarle in luogo di chiamata ma non centralizzate. La mia politica è di evitare l'uso di eccezioni controllate il più possibile. Si consideri IOException su Closable.close(): è utile o conveniente? I casi in cui è molto raro, ma ogni sviluppatore Java nel mondo è costretto ad occuparsene. Il più delle volte viene inghiottito o registrato al meglio. E immagina quanto aggiunge allo code size! Ci sono alcuni post su eccezioni controllate dal loro lato oscuro:

  1. "Does Java need Checked Exceptions?" by Bruce Eckel
  2. The Trouble with Checked Exceptions A Conversation with Anders Hejlsberg, by Bill Venners with Bruce Eckel
  3. Java Design Flaws, C2 wiki

Ci sono casi in cui eccezioni controllate viene in soccorso. Ma a mio parere sono rari e di solito riguardano l'implementazione di alcuni moduli specifici. E non danno molto profitto.

3

Come qualcuno che ama Java eccezioni controllate, penso che il problema (Java difetto di progettazione se si vuole) è che le librerie standard non supportano un tipo Iterator generica in cui il metodo next genera un'eccezione verificata.

Il codice di base per Sesame ha una classe variante Iterator denominata Iterable che fa proprio questo. La firma è la seguente:

 
Interface Iteration<E,X extends Exception> 

Type Parameters: 
    E - Object type of objects contained in the iteration. 
    X - Exception type that is thrown when a problem occurs during iteration. 

Questo sembra funzionare nel contesto limitato di sesamo, in cui vengono utilizzati sottoclassi specifiche che "fissare" il parametro di tipo di eccezione. Ma (ovviamente) non si integra con i tipi di raccolta standard, la nuova sintassi del ciclo for di Java 5 e così via.

2

hasNext non dovrebbe generare un'eccezione: dovrebbe verificare che sia corretto procedere e restituire un valore booleano su quella base (registrerei eventuali eccezioni sottostanti con un programma di registrazione). Vorrei lanciare un RuntimeException se il prossimo tentativo fallisce (e registrare l'eccezione verificata con un logger). Next non dovrebbe fallire in condizioni normali se il test è ok e se il test fallisce non si dovrebbe chiamare il prossimo (quindi eccezione di runtime).

1

Questa domanda è stato chiesto molto tempo fa, ma mi piacerebbe avere un feedback su un modello che può essere utile Qui. Un'interfaccia aggiuntiva o classe ExceptionHandler potrebbe essere composta all'interno della classe che implementa Iterator e potrebbe essere implementata dal client per indirizzare le eccezioni come desiderato.

public interface ExceptionHandler { 
    // Log, or rethrow, or ignore... 
    public Object handleException(Exception e); 
} 

classi che implementano potrebbe fare qualcosa di simile a questi:

public class ExceptionRethrower implements ExceptionHandler { 
    @Override 
    public Object handleException(Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

public class ExceptionPrinter implements ExceptionHandler { 
    @Override 
    public Object handleException(Exception e) { 
     System.err.println(e); 
     return e; 
    } 
} 

Per un iteratore che legge da un file, il codice può essere simile a questa:

public class MyReaderIterator implements Iterator<String> { 
    private final BufferedReader reader; 
    private final ExceptionHandler exceptionHandler; 
    public MyReaderIterator(BufferedReader r, ExceptionHandler h) { 
     reader = r; 
     exceptionHandler = h; 
    } 
    @Override 
    public String next() { 
     try { 
      return reader.readLine(); 
     } catch (IOException ioe) { 
      Object o = exceptionHandler.handleException(ioe); 
      // could do whatever else with o 
      return null; 
     } 
    } 
    // also need to implement hasNext and remove, of course 
} 

Che cosa la gente pensa ? Vale la pena di questo livello di indirezione, o è una violazione di stratificazione e semplicemente troppo complicata? Il nome dell'interfaccia potrebbe non essere esattamente appropriato (forse ExceptionProcessor o ExceptionInterceptor poiché può interagire ma non gestire completamente l'eccezione, ci deve ancora essere un po 'di gestione nella classe in cui è composta).

Sono d'accordo sul fatto che tutto ciò sembra solo una soluzione al fatto che Iterator.next() non è stato dichiarato per generare eccezioni, e mi fa diventare pino per i generatori e il modello di eccezione di python.

0

Io usally uso

@Override 
void remove() { 
    throws new UnsupportedOperationException("remove method not implemented"); 
} 

nota: UnsupportedOperationException è un RuntimeException

Dal momento che si properbly puntando qualcosa come

for (IterElem e : iterable) 
    e.action(); 

vi andrà bene

1

Se davvero non voglio usare l'eccezione di runtime, è possibile utilizzare NoSuchElementException. Hai il permesso di usarlo in modalità override next() perché è stata dichiarata per essere gettato in Iterator definizione (ho provato questo e il codice compilato). Ma dovresti considerare se è semanticamente valido per il tuo caso.

+0

Credo che questo sia il miglior eccezione a gettare per errori iteratore – smac89

+0

'NoSuchElementException' è un' RuntimeException'. Potresti anche creare la tua estensione personalizzata di 'RuntimeException', sarebbe la stessa cosa, giusto? – boumbh

-1

Nella mia mente, iteratori sono destinate ad iterare, essi non sono destinati a calcolo, quindi l'assenza di throws sul metodo next().

Ti stai chiedendo come utilizzare l'interfaccia per fare qualcosa che non è stato progettato per.

Eppure, se il Iterator è destinato ad essere usato da voi (che è generalmente una cattiva ipotesi), si potrebbe definire un safeNext() metodo pubblico in questo modo:

public Element safeNext() throws YourCheckedException { 
    ... 
} 

@Override 
public Element next() { 
    try { 
     return safeNext(); 
    } catch (YourException e) { 
     throw new YourRuntimeException("Could not fetch the next element", e); 
    } 
} 

Poi, quando si utilizza il Iterator è possibile scegliere di utilizzare safeNext() e gestire l'eccezione verificata o utilizzare next() come si farebbe con qualsiasi iteratore.

In breve, non è possibile avere sia la comodità dell'interfaccia iteratore (non è possibile prevedere che gli oggetti Iterable utilizzeranno il metodo safeNext()) e il lancio di eccezioni verificate.