I'd like to implement an iterator that retrieves objects from disk/network.
Iterator itr = getRemoteIterator();
while(itr.hasNext()) {
Object element = itr.next();
System.out.print(element + " ");
}
However the problem is that hasNext()
and next()
methods of the Iterator object does not allow to throw IOException
. Is there any other standard interface work around this issue?
Desired code is:
public interface RemoteIterator<E> {
boolean hasNext() throws IOException;
E next() throws IOException;
void remove();
}
It isn't exactly what you want, but consider:
This is happening over a network, so it may be slow. You should be probably be using a Future
(or similar).
So, instead of having your RemoteIterator<E>
where next()
returns a E, use
RemoteIterator<Future<E>>
, where it returns a Future<E>
.
In the Future.get()
method, you can wrap any IOException
in an ExecutionException
.
Not perfect, but Future.get()
implies that things may be slow, plus it does throw a checked exception which will tip off other programmers what is happening, and the natural response is to call getCause()
. So this avoids most of the "WTF" aspects.
Unfortunately you can't do this. Declared exceptions are part of a method's signature and while you can narrow them e.g.
interface FooCallable extends Callable<T> {
T call() throws IOException; // Narrows Exception from the parent interface
}
you can't introduce new throws clauses or widen the declared one.
Iterator
is a fundamental Java interface, used by enhanced for, so even if somehow you could do what you wanted, the compiler would need to know that
for (T obj: ioExceptionThrowingIterable) { ... }
requires the checked IOException
to be caught or thrown.
It's matter of opinion, but I think you should use a custom subclass of RuntimeException
and document your interface carefully. Checked exceptions are avoided in modern frameworks, e.g. Spring, because of the problems associated with them.
Edit: Borrowed and adapted from user949300's answer, you could wrap the returned object in e.g.
interface RemoteObject<T> {
T get() throws IOException
}
Then return an Iterator<RemoteObject<T>>
, and force the caller to handle the IOException
when unwrapping with get
:
for (RemoteObject<Foo> obj: iterable) {
try {
foo = obj.get()
} catch (IOException ex) {
/*
* 9 times out of 10, the lazy user just prints the stack trace
* or something else inappropriate
* which is why I despise checked exceptions
*/
}
}
In this case, there's no need to extend Iterator
, it can be used directly. In fact, you can use Collection
, if applicable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With