9

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();
}
emory
  • 10,725
  • 2
  • 30
  • 58
Anthony
  • 12,407
  • 12
  • 64
  • 88
  • 10
    Why is it of such importance that the exception type be `IOException`? Why not choose an unchecked exception? – Marko Topolnik Oct 09 '12 at 13:54
  • 2
    Because I'm using network connection. Unchecked exception is an anti-pattern in this situation. – Anthony Oct 09 '12 at 13:55
  • 4
    OK, so it boils down just to your opinion? I suggest adjusting your opinion, then, since you cannot achieve what you want with `java.util.Iterator` and there are no other iterator interfaces in the JDK. – Marko Topolnik Oct 09 '12 at 13:56
  • 4
    Using `java.util.Iterator` to throw `IOException`s is a really, really bad code smell. Write your own interface, or better, find another approach. – Louis Wasserman Oct 09 '12 at 18:25
  • 1
    IMHO throwing IO-Exception would be an anti-pattern here. You could either throw NoSuchElementException (or a derived class), or a RuntimeException. Anything else would break the contract you made when implementing Iterator. Only alternative I see is not having RemoteIterator extend Iterator. Then you are free to throw whatever you like. – Axel Oct 12 '12 at 07:26
  • Note that there is `UncheckedIOException` in `java.io` – bcoughlan Dec 04 '14 at 12:41

11 Answers11

8

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.

artbristol
  • 32,010
  • 5
  • 70
  • 103
  • 1
    Very good answer, however does not look as a solution. RuntimeException means that programmer made a mistake: NullPointerException, IndexOutOfBoundsException, ArithmeticException (division by zero), etc. But IOException can happen even if the algorithm is correct. That's why "modern" frameworks will never become part of Java specification. – Anthony Oct 16 '12 at 21:04
  • @Antonio thanks for chiming in. Bear in mind that no other major language uses checked exceptions. In my opinion, they are a failed experiment, responsible for countless bugs. – artbristol Oct 16 '12 at 21:14
  • @Antonio I was deliberately using the standard Java interface `Callable`, not a custom one – artbristol Oct 16 '12 at 21:20
  • 1
    @Antonio your assumption "RuntimeException means the programmer made a mistake" is incorrect. – Thorbjørn Ravn Andersen Oct 17 '12 at 20:04
  • 1
    That's not my opinion, but opinion of Joshua Bloch (one of Java authors). – Anthony Oct 17 '12 at 20:56
8

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.

artbristol
  • 32,010
  • 5
  • 70
  • 103
user949300
  • 15,364
  • 7
  • 35
  • 66
  • 1
    Isn't the problem here that the `hasNext()` method has to do I/O as well? Future doesn't solve that problem. – Markus May 08 '14 at 17:31
  • @Markus Wüstenberg If you have an *iterator* with a `hasNext()` method which throws an exception, you can always extend it into an *iterator* which doesn't by caching the result of `hasNext()` in a `boolean` class attribute (once in the constructor, and once at the end of the `next()` method). Edit: My bad, I’m just moving the problem back to the `next()` method. – boumbh Oct 19 '15 at 10:09
3

Next() is throwing http://docs.oracle.com/javase/7/docs/api/java/util/NoSuchElementException.html, You can write a custom Exception class, catching that exception and then throw the IOException.

sheidaei
  • 9,842
  • 20
  • 63
  • 86
1

I would rather suggest that getRemoteIterator() method should ideally throw an IOException, since objects retrieval will happen inside the method.

1

Iterator is a well-know design pattern. If the SDK Iterator doesn't fit your current needs, you could make an iterator on your own.

I find a nice explanation and sample code in this site. You can take that code as reference and make your own implementation of the Iterator Interface you need.

Carlos Gavidia-Calderon
  • 7,145
  • 9
  • 34
  • 59
1

The basic problem is that you want to be able to throw a checked exception through an API which is not designed to do so.

The only legitimate way to get this to work without black magic is to wrap with unchecked exceptions. The simplest is RuntimeException:

} catch (IOException e) {
   throw new RuntimeException("failed in " + fileName, e);
}

You can then catch RuntimeException in the calling code and look at the getCause() method.

If you need explicitly to be able to tell for sure, that this is your exception then consider creating FooBarDomainException extends RuntimeException and implement the constructors you need delegating to super(...,e). You can then catch FooBarException instead and handle getCause() as above.

I will advise you against using magic to do this. When magic breaks, it tends to be in very non-obvious ways which are hard to find and fix. Additionally it will be harder for others to maintain.

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
1

Here is my contribution and at the bottom the stack.

It's not really what you want but it's near.

public class IOIterator<E>
   implements
      Iterator<E>
{
   @Override
   public boolean hasNext()
   {
      return index < 5;
   }

   @Override
   public E next()
   {
      try
      {
         if( index++ > 3)
         {
            throw new IOException( "True fault origin" );
         }
      }
      catch( Throwable t )
      {
         NoSuchElementException nse = new NoSuchElementException( "index: " + index );
         nse.initCause( t );
         throw nse;
      }
      return null;
   }

   @Override
   public void remove()
   {
      if( index == 0 ) throw new IllegalStateException( "call next first!" );
      try
      {
         if( index == 4 )
         {
            throw new IOException( "True fault origin" );
         }
      }
      catch( Throwable t )
      {
         UnsupportedOperationException uoe = new UnsupportedOperationException( "index: " + index );
         uoe.initCause( t );
         throw uoe;
      }
   }

   private int index;



   public static void main( String[] args )
   {
      IOIterator<Socket> it = new IOIterator<>();
      while( it.hasNext())
      {
         Socket socket = it.next();

      }
   }

}

And the exception stack trace:

Exception in thread "main" java.util.NoSuchElementException: index: 5
    at hpms.study.IOIterator.next(IOIterator.java:32)
    at hpms.study.IOIterator.main(IOIterator.java:67)
Caused by: java.io.IOException: True fault origin
    at hpms.study.IOIterator.next(IOIterator.java:27)
    ... 1 more
Aubin
  • 14,617
  • 9
  • 61
  • 84
0

I suggest making your own interface which is structurally similar to Iterator but has the added Interface constraint that you wanted.

It sounds like what you really have here is a Stream of objects. I'd go that route, with the attendant expectation that sometimes you'll get an EOFException or the like.

Jason
  • 3,021
  • 1
  • 23
  • 25
0

There is one more approach to this problem. The main idea is to extend NoSuchElementException:

class RemoteIOException extends NoSuchElementException {
    ...
}

The disadvantage is that NoSuchElementException extends RuntimeException.

Anthony
  • 12,407
  • 12
  • 64
  • 88
-1

checked exception is just a syntax limit

code below from lombok http://www.projectlombok.org/

you will get

Exception in thread "main" java.io.IOException at B.main(B.java:19)

public static RuntimeException sneakyThrow(Throwable t) {
    if (t == null) throw new NullPointerException("t");
    B.<RuntimeException>sneakyThrow0(t);
    return null;
}

@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
    throw (T)t;
}

public static void main(String[] args) {
    throw sneakyThrow(new IOException());
}
farmer1992
  • 7,816
  • 3
  • 30
  • 26
  • Why downvoted? I was thinking of almost the same. And, in fact, is the only answer giving a valid solution for OP's requisites. – sinuhepop Oct 14 '12 at 23:12
  • 2
    One day the user of this code - ignorant of the black magic inside - is going to have a WTF moment. – emory Oct 15 '12 at 16:23
  • @emory: can you please be more explicit? What will he get? A checked exception that is not declared? Yes, but this is exactly what the OP is asking for. – sinuhepop Oct 15 '12 at 20:05
  • @sinuhepop This whole question is a WTF. Expect the solution to appear on TheWailyWTF in a few years when Antonio gets promoted for his great job gaming the Java compiler here. All this over a 3-method interface? :( – Christopher Schultz Oct 15 '12 at 22:02
  • @sinuhepop you are correct - a checked exception that is not declared. If the OP has the WTF moment then shame on him for not understanding his own black magic. OTH if the OP's coworkers, successor, etc have a WTF moment then can we really blame them? – emory Oct 16 '12 at 02:02
-1

I posted some sample code on ideone. The basic idea is to create a custom RuntimeException class IOIteratorException that only excepts an IOException as the cause. Then you can write code like this

try
{
      iterator . next ( ) ;
}
catch ( IOIteratorException cause )
{
      cause . getCause ( ) ; // the compiler knows this is an IOException so you don't need to cast it
}
emory
  • 10,725
  • 2
  • 30
  • 58