Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a Java Iterator that throws IOException

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();
}
like image 336
Antonio Avatar asked Oct 09 '12 13:10

Antonio


2 Answers

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.

like image 172
user949300 Avatar answered Oct 27 '22 00:10

user949300


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.

like image 45
artbristol Avatar answered Oct 26 '22 23:10

artbristol