The java.lang.Iterator
interface has 3 methods: hasNext
, next
and remove
. In order to implement a read-only iterator, you have to provide an implementation for 2 of those: hasNext
and next
.
My problem is that these methods does not declare any exceptions. So if my code inside the iteration process declares exceptions, I must enclose my iteration code inside a try/catch block.
My current policy has been to rethrow the exception enclosed in a RuntimeException
. But this has issues because the checked exceptions are lost and the client code no longer can catch those exceptions explicitly.
How can I work around this limitation in the Iterator class?
开发者_如何学运维Here is a sample code for clarity:
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);
}
}
...
}
You should rethrow exception as custom runtime exception, not generic one, for instance SomethingBadRuntimeException
. Also, you can try exception tunneling.
And I'm assured that forcing client to deal with exceptions by making them checked is a bad practice. It just pollutes your code or forces to wrap exceptions with runtime ones or force to process them in place of call but not centralized. My policy is to avoid using checked exceptions as much as possible. Consider IOException
on Closable.close()
: is it useful or convenient? Cases when it is are very rare, but every Java developer in the world is forced to deal with it. Most often it is swallowed or logged at best. And imagine how much this adds to code size!
There's some posts about checked exceptions from their dark side:
- "Does Java need Checked Exceptions?" by Bruce Eckel
- The Trouble with Checked Exceptions A Conversation with Anders Hejlsberg, by Bill Venners with Bruce Eckel
- Java Design Flaws, C2 wiki
There are cases when checked exceptions comes to rescue. But in my opinion they are rare and usually concern to implementation of some specific module. And they don't give very much profit.
I've implemented a lot of Iterator, sometimes on top of with-check-exception iterators (ResultSet is conceptually a record iterator, InputStream y conceptually a byte iterator) and so on. It's very very nice and handy (you can implement pipe & filters architecture for a LOT of things).
If you prefer to declare your exceptions, then declare a new type of Iterator (ExceptionIterator, it would be like Runnable and Callable). You can use it along or your code but you can't compose it with outside components (Java Class Library or 3d party libs).
But if you prefer to use super-standard interfaces (like iterator) to use them anywhere, then use Iterator. If you know your Exceptions will be a condition for stop your processing, or you don't mind a lot... use them.
Runtime exceptions are not so terrible. By example. Hibernate use them to implement proxies and stuff like that. They have to except DB exceptions but can't declare them in their implementations of List.
As someone who likes Java checked exceptions, I think that the problem (Java design flaw if you will) is that the standard libraries don't support a generic Iterator type where the next
method throws a checked exception.
The code base for Sesame has an Iterator variant class called Iterable that does just that. The signature is as follows:
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.
This seems to work in the limited context of Sesame, where specific subclasses are used that "fix" the exception type parameter. But (of course) it doesn't integrate with the standard collection types, Java 5's new for
loop syntax, and so on.
hasNext should not really throw an exception - it should check that it is ok to proceed and return a boolean value on that basis (I would log any underlying exceptions with a logger). I would throw a RuntimeException if next fails (and log the checked exception with a logger). Next should not fail in normal conditions if the test was ok and if the test fails you should not call next (hence runtime exception).
If you really don't want to use Runtime exception, you can use NoSuchElementException
. You are allowed to use it in overridden next()
method because it is declared to be thrown in
Iterator
definition (I tried this and the code compiled). But you should consider whether it is semantically valid for your case.
If your implementation doesn't have the properties required by the Iterator interface, why do you want to use it?
I see no other solution than the RuntimeException (with it's issues).
This question was asked a long time ago, but I would like to have feedback on a pattern that may be useful here. An additional interface or class ExceptionHandler
could be composed within the class which implements Iterator and could be implemented by the client to address the exceptions as desired.
public interface ExceptionHandler {
// Log, or rethrow, or ignore...
public Object handleException(Exception e);
}
Implementing classes could do something like these:
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;
}
}
For an iterator that reads from a file, the code may look something like this:
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
}
What do people think? It is worth this level of indirection, or is it a layering violation and just plain too complicated? The name of the interface may not be exactly appropriate (perhaps ExceptionProcessor or ExceptionInterceptor since it can interact with but not fully handle the exception, there still needs to be some handling in the class into which it is composed).
I agree that this all just seems like a workaround to the fact that Iterator.next() wasn't declared to throw exceptions, and makes me pine for generators and python's exception model.
Whatever you do, do not use this trick :-)
http://blog.quenta.org/2006/11/generic-exceptions.html
I usally use
@Override
void remove() {
throws new UnsupportedOperationException("remove method not implemented");
}
note: UnsupportedOperationException is a RuntimeException
Since you are properbly aiming for something like
for (IterElem e : iterable)
e.action();
You will be fine
In my mind, iterators are meant to iterate, they are not meant to compute, hence the absence of throws
on the next()
method.
You are asking how to use the interface to do something it was not designed for.
Still, if the Iterator
is aimed to be used by yourself (which is generally a bad assumption), you could define a public safeNext()
method like this:
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);
}
}
Then, when using the Iterator
you can choose to use safeNext()
and handle the checked exception, or use next()
as you would with any iterators.
In short, you can’t have both the convenience of the iterator interface (you can't expect that Iterable
objects will use the safeNext()
method), and the checked exception throwing.
精彩评论