处理多个异常

8
我编写了一个类,用于加载应用程序的配置对象,并跟踪它们,以便我可以轻松地通过单个方法调用编写更改或重新加载整个配置。但是,每个配置对象在进行IO时都可能会引发异常,但我不希望这些错误取消整个过程,以便为其他对象提供重新加载/编写的机会。因此,当迭代对象时,我收集所有引发的异常并将它们存储在超级异常中,在循环之后抛出该异常,因为每个异常仍然必须处理,并且必须通知某人确切地出了什么问题。但是,这种方法对我来说看起来有点奇怪。是否有其他更简洁的解决方案?
以下是所述类的一些代码:
public synchronized void store() throws MultipleCauseException
    {
    MultipleCauseException me = new MultipleCauseException("unable to store some resources");
    for(Resource resource : this.resources.values())
        {
        try
            {
            resource.store();
            }
        catch(StoreException e)
            {
            me.addCause(e);
            }
        }
    if(me.hasCauses())
        throw me;
    }
2个回答

4
如果你想保留操作的结果,似乎你是有这个意图的,那么抛出异常就不是正确的做法。通常情况下,如果你抛出异常,应该尽量不去影响其他代码。
我的建议是在进行操作时,将异常或从异常中派生的数据传递给错误处理回调函数。
public interface StoreExceptionHandler {
    void handle(StoreException exc);
}

public synchronized void store(StoreExceptionHandler excHandler) {
    for (Resource resource : this.resources.values()) {
        try {
            resource.store();
        } catch (StoreException exc) {
            excHandler.handle(exc);
        }
    }
    /* ... return normally ... */
]

考虑到重新加载,抛出异常的对象代表着最新可能的一致状态(即保留配置的旧副本)。 - the-banana-king

3
设计何时以及应该抛出什么样的异常有一些指导原则,对于这种情况,其中两个相关的原则是:
  • 抛出适合抽象化的异常(即异常转换范式)
  • 尽可能早地抛出异常
你将 StoreException 翻译为 MultipleCauseException 的方式我认为是合理的,尽管将不同类型的异常合并到一个中可能不是最好的选择。不幸的是,Java 不支持通用的 Throwable,因此可能唯一的选择是创建一个单独的 MultipleStoreException 子类。
关于尽早抛出异常(你没有这样做),我会说在某些情况下弯曲规则是可以的。我觉得延迟抛出的危险是异常情况不必要地嵌套成链式反应。在可能的情况下,您希望避免这种情况,并将异常局限于可能的最小范围内。
在您的情况下,如果将资源存储概念上视为多个独立任务,那么以您的方式“批处理”异常可能是可以的。然而,在任务具有更复杂的相互依赖关系的其他情况下,将所有任务合并在一起会使分析异常的任务更加困难。
从更抽象的角度来看,在图论术语中,我认为将具有多个无子节点的孩子节点的节点合并到一个节点中是可以接受的。将整个大的子树甚至更糟糕的是,将循环图合并为一个节点可能不是可行的。

在Java中,你不能将异常泛型化。 - Tom Hawtin - tackline
已经尝试过并感到惊讶。然而,我没有看到任何理由,因为泛型只是扩展为一些隐式转换,在这里真的很有帮助。也许有人认为“我们有类型擦除和类似catch(GenericException<FooException> e) { } catch (GenericException<BarException> e) { }这样的东西不起作用,所以我们防止程序员甚至尝试”。 - the-banana-king

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接