2010-02-05 13 views
25

La interfaz java.lang.Iterator tiene 3 métodos: hasNext, next y remove. Para implementar un iterador de solo lectura, debe proporcionar una implementación para 2 de ellos: hasNext y next.Qué hacer con las excepciones al implementar java.lang.Iterator

Mi problema es que estos métodos no declaran ninguna excepción. Entonces, si mi código dentro del proceso de iteración declara excepciones, debo encerrar mi código de iteración dentro de un bloque try/catch.

Mi política actual ha sido volver a lanzar la excepción incluida en un RuntimeException. Pero esto tiene problemas porque las excepciones comprobadas se pierden y el código del cliente ya no puede capturar esas excepciones de forma explícita.

¿Cómo puedo evitar esta limitación en la clase Iterator?

Aquí hay un código de ejemplo para mayor claridad:

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); 
     } 
    } 

    ... 
} 

Respuesta

9

Implementé mucho Iterator, algunas veces encima de los iteradores con check-exception (ResultSet es conceptualmente un iterador de registro, InputStream y conceptualmente un iterador de bytes) y así sucesivamente. Es muy, muy agradable y práctico (puede implementar la arquitectura de filtros de tubería & para MUCHAS cosas).

Si prefiere declare sus excepciones, luego declare un nuevo tipo de iterador (ExceptionIterator, sería como Runnable e invocable). Puedes usarlo junto con tu código pero no puedes componerlo con componentes externos (Java Class Library o 3d party libs).

Pero si prefiere usar interfaces super-estándar (como iterador) para usarlas en cualquier lugar, entonces use Iterator. Si sabe que sus Excepciones serán una condición para detener su procesamiento, o no le importa mucho ... úselos.

Las excepciones de tiempo de ejecución no son tan terribles. Por ejemplo. Hibernate los usa para implementar proxies y cosas así. Deben exceptuar las excepciones de base de datos pero no pueden declararlas en sus implementaciones de lista.

+1

Otra opción, si no puede controlar el código del cliente UI: cree un iterador que registre las excepciones en algún lugar accesible por un getter o algo, o llame a una interfaz de devolución de llamada, o algo que controle y pueda mostrar el error en cualquier lugar. Si eso es útil para usted, puede crear un "CatcherIterator" que simplemente envuelve un runtime-exception-thrower-iterator y atrapa todas las excepciones posibles redirigiéndolas a la propiedad o interfaz de devolución de llamada. – helios

1

Si su aplicación no tiene las propiedades requeridas por la interfaz Iterator, ¿por qué quiere usarlo?

No veo otra solución que la excepción RuntimeException (con sus problemas).

11

Debe volver a lanzar la excepción como excepción de tiempo de ejecución personalizada, no genérica, por ejemplo SomethingBadRuntimeException. Además, puedes probar exception tunneling.

Y estoy seguro de que obligar al cliente a hacer frente a las excepciones haciéndolos verificados es una mala práctica. Simplemente contamina el código o las fuerzas para ajustar las excepciones con las de tiempo de ejecución o forzarlas a procesarlas en lugar de llamarlas, pero no de forma centralizada. Mi política es evitar el uso de excepciones comprobadas tanto como sea posible. Considere IOException en Closable.close(): ¿es útil o conveniente? Los casos en los que es muy raro, pero todos los desarrolladores de Java en el mundo se ven obligados a lidiar con él. La mayoría de las veces se traga o registra en el mejor de los casos. ¡Y imagina cuánto esto se suma a code size! Hay algunos mensajes sobre excepciones controladas desde su lado 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

hay casos en que las excepciones controladas viene a rescatar. Pero, en mi opinión, son raros y generalmente se refieren a la implementación de algún módulo específico. Y no dan mucho beneficio.

3

Como alguien que le gusta Java excepciones comprobadas, creo que el problema (error de diseño de Java si se quiere) es que las bibliotecas estándar no son compatibles con un tipo de iterador genérico donde el método next lanza una excepción comprobada.

La base de código para Sesame tiene una clase de variante de iterador llamada Iterable que hace justamente eso. La firma es la siguiente:

 
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. 

Esto parece funcionar en el contexto limitado de sésamo, donde se utilizan subclases específicas que "fijar" el parámetro de tipo de excepción. Pero (por supuesto) no se integra con los tipos de colección estándar, la nueva sintaxis de bucle for de Java 5, y así sucesivamente.

2

hasNext no debería arrojar una excepción: debería verificar que está bien proceder y devolver un valor booleano sobre esa base (registraría cualquier excepción subyacente con un registrador). Lanzaría una RuntimeException si la próxima falla (y registraría la excepción marcada con un registrador). Next no debe fallar en condiciones normales si la prueba fue correcta y si la prueba falla, no debe llamar a continuación (por lo tanto, excepción de tiempo de ejecución).

1

se le pidió Esta pregunta hace mucho tiempo, pero me gustaría tener información sobre un patrón que puede ser útil aquí. Una interfaz adicional o clase ExceptionHandler se podría componer dentro de la clase que implementa Iterator y podría ser implementada por el cliente para abordar las excepciones como se desee.

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

clases de aplicación puedan hacer algo como esto:

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; 
    } 
} 

Para un iterador que se lee de un archivo, el código puede ser algo como esto:

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 
} 

¿Qué piensan las personas ? Vale la pena este nivel de indirección, ¿o es una violación de capas y simplemente demasiado complicado? El nombre de la interfaz puede no ser exactamente apropiado (quizás ExceptionProcessor o ExceptionInterceptor, ya que puede interactuar con la excepción pero no manejarla por completo, aún debe haber algún manejo en la clase en la que está compuesta).

Acepto que todo esto parece una solución al hecho de que Iterator.next() no fue declarado para lanzar excepciones, y me hace pensar en los generadores y el modelo de excepción de python.

0

utilizo Normalamente

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

nota: UnsupportedOperationException es un RuntimeException

Puesto que usted está properbly apuntando para algo así como

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

Va a estar bien

1

Si realmente no desea usar la excepción de tiempo de ejecución, puede usar NoSuchElementException. Se le permite usarlo en el método next() reemplazado porque se declaró que se lanzó en Iterator definición (Intenté esto y el código compilado). Pero debe considerar si es semánticamente válido para su caso.

+0

Creo que esta es la mejor excepción para arrojar errores de iterador – smac89

+0

'NoSuchElementException' es una' RuntimeException'. También podría crear su propia extensión personalizada de 'RuntimeException', sería lo mismo, ¿verdad? – boumbh

-1

En mi mente, iteradores tienen el propósito de iterate, que no están destinados a de cómputo, por lo tanto, la ausencia de throws en el método next().

Usted está preguntando cómo usar la interfaz para hacer algo para lo que no fue diseñada.

Sin embargo, si el Iterator está dirigido para ser utilizado por sí mismo (que es generalmente una mala suposición), se podría definir un método público safeNext() así:

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); 
    } 
} 

Entonces, cuando se utiliza el Iterator que pueda elija usar safeNext() y maneje la excepción marcada, o use next() como lo haría con cualquier iterador.

En resumen, no se puede tener la conveniencia de la interfaz del iterador (no se puede esperar que los objetos Iterable usen el método safeNext()) y el lanzamiento de excepción comprobada.

Cuestiones relacionadas