代码之家  ›  专栏  ›  技术社区  ›  Vincent Robert

实现java.lang.迭代器时如何处理异常

  •  29
  • Vincent Robert  · 技术社区  · 15 年前

    这个 java.lang.Iterator 接口有3种方法: hasNext , next remove . 为了实现只读迭代器,必须提供其中2个的实现: 哈萨特 下一个 .

    我的问题是这些方法没有声明任何异常。因此,如果迭代过程中的代码声明了异常,那么我必须将迭代代码包含在一个try/catch块中。

    我目前的政策是重新引发 RuntimeException . 但这有问题,因为检查的异常丢失了,客户机代码不再能够显式捕获这些异常。

    我如何在迭代器类中解决这个限制?

    为了清晰起见,下面是一个示例代码:

    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);
            }
        }
    
        ...
    }
    
    10 回复  |  直到 9 年前
        1
  •  9
  •   helios    15 年前

    我实现了很多迭代器,有时在WITHCHECK异常迭代器之上(resultset概念上是记录迭代器,inputstream概念上是字节迭代器),等等。这是非常好和方便(您可以实现很多事情的管道和过滤器架构)。

    如果你愿意的话 宣布你的例外 ,然后声明新类型的迭代器(exception迭代器,它类似于runnable和callable)。您可以使用它或您的代码,但不能用外部组件(Java类库或3D方LIBs)编写它。

    但是如果你喜欢用 超标准接口 (类似于迭代器)要在任何地方使用它们,请使用迭代器。如果您知道您的异常将是停止处理的一个条件,或者您不太介意…使用它们。

    运行时异常并不那么可怕。 举个例子。Hibernate使用它们来实现代理和类似的东西。它们必须排除DB异常,但不能在列表的实现中声明它们。

        2
  •  11
  •   Rorick    15 年前

    例如,应将异常作为自定义运行时异常而不是一般异常重新引发。 SomethingBadRuntimeException . 另外,你也可以尝试 exception tunneling .

    我敢保证,强迫客户通过检查异常来处理异常是一种不好的做法。它只会污染您的代码,或者强制用运行时的异常包装异常,或者强制在调用位置处理异常,但不集中处理。我的策略是尽可能避免使用选中的异常。考虑 IOException Closable.close() :它有用还是方便?当它是非常罕见的情况下,但是每个Java开发者在世界上被迫处理它。通常情况下,它最多被吞咽或记录下来。想象一下这能增加多少 code size ! 有一些关于检查异常的帖子来自他们的阴暗面:

    1. "Does Java need Checked Exceptions?" by Bruce Eckel
    2. The Trouble with Checked Exceptions A Conversation with Anders Hejlsberg, by Bill Venners with Bruce Eckel
    3. Java Design Flaws, C2 wiki

    有些情况下,检查异常来救援。但在我看来,它们很少,而且通常涉及到某些特定模块的实现。而且他们也没有给多少利润。

        3
  •  3
  •   Stephen C    15 年前

    作为某人 喜欢 Java检查异常,我认为问题(Java设计缺陷,如果您愿意)是标准库不支持泛型迭代器类型 next 方法引发一个选中的异常。

    的代码基础 Sesame 调用了迭代器变量类 Iterable 就是这样。 签字如下:

    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.
    

    这似乎适用于芝麻的有限上下文,其中使用了“修复”异常类型参数的特定子类。但是(当然)它不与标准的集合类型集成,Java 5的新的 for 循环语法等等。

        4
  •  2
  •   ishmeister    15 年前

    HasNext不应该真的抛出异常——它应该检查是否可以继续,并在此基础上返回一个布尔值(我将用一个记录器记录任何底层异常)。如果next失败,我将抛出runtimeexception(并用记录器记录检查的异常)。如果测试正常,next不应失败;如果测试失败,则不应调用next(因此出现运行时异常)。

        5
  •  1
  •   Marcel Jackwerth    15 年前

    如果实现没有迭代器接口所需的属性,为什么要使用它?

    除了RuntimeException(它的问题),我没有看到其他解决方案。

        6
  •  1
  •   bpow    12 年前

    这个问题很久以前就被问到了,但是我想得到一个模式的反馈,这个模式在这里可能很有用。附加接口或类 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,因为它可以与异常交互,但不能完全处理异常,因此在它所组成的类中仍然需要一些处理)。

    我同意这一切只是一个解决方法,它解决了迭代器.next()没有声明为抛出异常的事实,并且使我对生成器和Python的异常模型很感兴趣。

        7
  •  1
  •   nuoritoveri    10 年前

    如果确实不想使用运行时异常,可以使用 NoSuchElementException . 您可以在重写中使用它 next() 方法,因为它被声明为 Iterator 定义(我尝试了这个方法并编译了代码)。但你应该考虑一下 语义上 对你的案件有效。

        8
  •  0
  •   finnw    15 年前

    不管你做什么,都要做 使用这个技巧:—)

    http://blog.quenta.org/2006/11/generic-exceptions.html

        9
  •  0
  •   smnsvane    12 年前

    我通常使用

    @Override
    void remove() {
        throws new UnsupportedOperationException("remove method not implemented");
    }
    

    注意:UnsupportedOperationException是一个RuntimeException

    因为你的目标是

    for (IterElem e : iterable)
        e.action();
    

    你会没事的

        10
  •  -1
  •   boumbh    9 年前

    在我看来,迭代器是用来 迭代 他们不是故意的 计算 因此没有 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);
        }
    }
    

    然后,当使用 迭代器 你可以选择使用 SAFEXEXT() 并处理检查的异常,或使用 下() 就像处理任何迭代器一样。

    简而言之,您可以同时拥有迭代器接口的便利性(您不能期望 Iterable 对象将使用 SAFEXEXT() 方法),并引发选中的异常。