9

我想实现一个从磁盘/网络检索对象的迭代器。

Iterator itr = getRemoteIterator();
while(itr.hasNext()) {
    Object element = itr.next();
    System.out.print(element + " ");
}

然而问题是IteratorhasNext()对象的next()方法不允许 throw 。是否有任何其他标准接口可以解决此问题?IOException

所需的代码是:

public interface RemoteIterator<E> {
    boolean hasNext() throws IOException;
    E next() throws IOException;
    void remove();
}
4

11 回答 11

8

不幸的是,你不能这样做。声明的异常是方法签名的一部分,虽然您可以缩小它们,例如

interface FooCallable extends Callable<T> {
T call() throws IOException; // Narrows Exception from the parent interface
} 

您不能引入新的 throws 条款或扩大声明的条款。

Iterator是一个基本的 Java 接口,由增强的 for 使用,所以即使你可以做你想做的事,编译器也需要知道

for (T obj: ioExceptionThrowingIterable) { ... }

要求被检查IOException者被抓住或抛出。

这是意见问题,但我认为您应该使用自定义子类RuntimeException并仔细记录您的界面。在现代框架(例如 Spring)中避免了检查异常,因为它们存在相关问题。

编辑:借用并改编自user949300的答案,您可以将返回的对象包装在例如

interface RemoteObject<T> {
    T get() throws IOException
}

然后返回一个Iterator<RemoteObject<T>>,并强制调用者IOException在解包时处理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
     */
    }
}

这种情况下不需要扩展Iterator,直接使用即可。事实上,Collection如果适用,您可以使用 。

于 2012-10-13T15:19:39.733 回答
8

这不是您想要的,但请考虑:

这是通过网络发生的,所以它可能很慢。您应该可能正在使用Future (或类似的)。

所以,不要让你的RemoteIterator<E>wherenext()返回一个 E,而是使用

RemoteIterator<Future<E>>, 它返回一个Future<E>.

在该Future.get()方法中,您可以将任何内容包装IOExceptionExecutionException.

不完美,但Future.get()暗示事情可能会很慢,而且它确实会抛出一个检查异常,这会提示其他程序员正在发生的事情,自然的响应是调用getCause(). 所以这避免了大多数“WTF”方面。

于 2012-10-16T19:40:48.097 回答
3

Next() 正在抛出http://docs.oracle.com/javase/7/docs/api/java/util/NoSuchElementException.html,您可以编写一个自定义异常类,捕获该异常然后抛出 IOException。

于 2012-10-09T14:06:17.610 回答
1

我宁愿建议 getRemoteIterator() 方法最好抛出一个 IOException,因为对象检索将发生在方法内部。

于 2012-10-13T12:42:58.437 回答
1

迭代器是一种众所周知的设计模式。如果 SDK 迭代器不能满足您当前的需求,您可以自己制作一个迭代器。

我在这个网站上找到了一个很好的解释和示例代码。您可以将该代码作为参考,并自己实现所需的迭代器接口。

于 2012-10-13T13:03:23.177 回答
1

基本问题是您希望能够通过不是为此而设计的 API 引发检查异常。

在没有黑魔法的情况下让它工作的唯一合法方法是使用未经检查的异常进行包装。最简单的是 RuntimeException:

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

然后,您可以在调用代码中捕获 RuntimeException 并查看该getCause()方法。

如果您需要明确地确定这是您的例外,那么请考虑创建FooBarDomainException extends RuntimeException并实现您需要委派给的构造函数super(...,e)。然后,您可以改为捕获 FooBarException 并按getCause()上述方式处理。

我会建议你不要使用魔法来做到这一点。当魔法中断时,它往往以非常不明显的方式出现,很难找到和修复。此外,其他人将更难以维护。

于 2012-10-13T13:14:24.973 回答
1

这是我的贡献,在堆栈的底部。

这不是你真正想要的,但它就在附近。

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

      }
   }

}

和异常堆栈跟踪:

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
于 2012-10-13T15:33:06.083 回答
0

我建议您制作自己的界面,该界面在结构上与 Iterator 相似,但添加了您想要的界面约束。

听起来你在这里真正拥有的是一个对象流。我会走那条路,伴随着期望有时你会得到一个 EOFException 或类似的东西。

于 2012-10-16T19:15:00.450 回答
0

还有另一种解决这个问题的方法。主要思想是扩展NoSuchElementException

class RemoteIOException extends NoSuchElementException {
    ...
}

缺点是NoSuchElementException扩展RuntimeException

于 2012-10-16T21:37:11.977 回答
-1

已检查异常只是语法限制

以下代码来自 lombok http://www.projectlombok.org/

你会得到

B.main(B.java:19) 处的线程“main”java.io.IOException 中的异常

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());
}
于 2012-10-09T14:33:22.300 回答
-1

我在ideone上发布了一些示例代码。基本思想是创建一个自定义 RuntimeException 类,该类IOIteratorException仅排除 IOException 作为原因。然后你可以写这样的代码

try
{
      iterator . next ( ) ;
}
catch ( IOIteratorException cause )
{
      cause . getCause ( ) ; // the compiler knows this is an IOException so you don't need to cast it
}
于 2012-10-15T11:56:34.760 回答