java.lang.Iterator
界面有3种方法:hasNext
,next
和remove
。为了实现只读迭代器,您必须为其中的两个提供实现:hasNext
和next
。
我的问题是这些方法没有声明任何异常。因此,如果我在迭代过程中的代码声明异常,我必须将我的迭代代码包含在try / catch块中。
我目前的政策是重新抛出RuntimeException
中包含的例外情况。但这有问题,因为已检查的异常会丢失,客户端代码不再能明确捕获这些异常。
如何在Iterator类中解决此限制?
为清晰起见,这是一个示例代码:
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);
}
}
...
}
答案 0 :(得分:11)
您应该将异常重新抛出为自定义运行时异常,而不是通用异常,例如SomethingBadRuntimeException
。此外,您可以尝试exception tunneling。
我确信强制客户通过检查异常来处理异常是一种不好的做法。它只是污染你的代码或强制用运行时包装异常或强制处理它们代替呼叫而不是集中处理。我的政策是尽量避免使用已检查的例外。在IOException
上考虑Closable.close()
:它有用还是方便?这种情况非常罕见,但世界上每个Java开发人员都被迫处理它。大多数情况下,它最多被吞下或记录。想象一下,code size增加了多少!
有一些关于从黑暗面检查过例的帖子:
有些情况下,经过检查的例外情况可以解决。但在我看来,它们很少见,而且通常与实施某些特定模块有关。而且他们没有给予很多利润。
答案 1 :(得分:9)
我已经实现了很多Iterator,有时在with-check-exception迭代器之上(ResultSet在概念上是一个记录迭代器,InputStream在概念上是一个字节迭代器),依此类推。它非常非常好用(您可以为很多东西实现管道和过滤器架构)。
如果您希望声明您的异常,那么声明一种新类型的Iterator(ExceptionIterator,它就像Runnable和Callable)。您可以使用它或代码,但不能使用外部组件(Java类库或3d方库)进行组合。
但如果您更喜欢使用超标准接口(如迭代器)在任何地方使用它们,那么请使用Iterator。如果您知道您的例外将是停止处理的条件,或者您不介意...使用它们。
运行时异常并不是那么糟糕。举个例子。 Hibernate使用它们来实现代理和类似的东西。它们必须除了DB异常,但不能在它们的List实现中声明它们。
答案 2 :(得分:3)
作为喜欢 Java检查异常的人,我认为问题(Java设计缺陷,如果你愿意)是标准库不支持{{1}的通用Iterator类型抛出一个已检查的异常。
Sesame的代码库有一个名为Iterable的Iterator变体类,它就是这样做的。 签名如下:
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.
这似乎适用于Sesame的有限上下文,其中使用了“修复”异常类型参数的特定子类。但(当然)它没有与标准集合类型,Java 5的新next
循环语法等集成。
答案 3 :(得分:2)
hasNext不应该真正抛出异常 - 它应该检查是否可以继续并在此基础上返回一个布尔值(我会用记录器记录任何底层异常)。如果下一次失败,我会抛出RuntimeException(并使用记录器记录已检查的异常)。如果测试没问题,那么接下来不应该在正常情况下失败,如果测试失败,则不应该调用next(因此运行时异常)。
答案 4 :(得分:1)
如果您的实现没有Iterator接口所需的属性,为什么要使用它呢?
除了RuntimeException之外,我没有看到任何其他解决方案(包括它的问题)。
答案 5 :(得分:1)
很久以前就提出过这个问题,但我希望对这里可能有用的模式有所反馈。可以在实现Iterator的类中组成一个额外的接口或类ExceptionHandler
,并且可以由客户端实现,以根据需要解决异常。
public interface ExceptionHandler {
// Log, or rethrow, or ignore...
public Object handleException(Exception e);
}
实现类可以执行以下操作:
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;
}
}
对于从文件读取的迭代器,代码可能如下所示:
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
}
人们怎么想?值得这个间接层,或者它是一个分层违规,只是简单太复杂?接口的名称可能不完全合适(可能是ExceptionProcessor或ExceptionInterceptor,因为它可以与异常进行交互但不能完全处理异常,仍然需要在组成它的类中进行一些处理)。
我同意这一切似乎只是解决了Iterator.next()没有被声明为抛出异常的事实,并使我成为生成器和python的异常模型。
答案 6 :(得分:1)
如果您真的不想使用运行时例外,可以使用NoSuchElementException
。您可以在重写的next()
方法中使用它,因为它被声明为抛出
Iterator
定义(我试过这个和编译的代码)。但是你应该考虑它是否语义对你的情况有效。
答案 7 :(得分:0)
无论你做什么,不使用这个技巧: - )
答案 8 :(得分:0)
我通常使用
@Override
void remove() {
throws new UnsupportedOperationException("remove method not implemented");
}
注意:UnsupportedOperationException是RuntimeException
因为你正确地瞄准像
这样的东西for (IterElem e : iterable)
e.action();
你会没事的
答案 9 :(得分:-1)
在我看来,迭代器意味着迭代,它们并不意味着计算,因此throws
上缺少next()
方法
您正在询问如何使用界面执行不适合的操作。
但是,如果Iterator
旨在被您自己使用(这通常是一个不好的假设),您可以定义一个公共safeNext()
方法,如下所示:
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);
}
}
然后,当使用Iterator
时,您可以选择使用safeNext()
并处理已检查的异常,或者像使用任何迭代器一样使用next()
。
简而言之,您不能同时拥有迭代器接口的便利性(您不能期望Iterable
对象将使用safeNext()
方法),并且检查异常抛出。