异常处理的"切换"等价物

Lou*_*ono 7 java exception-handling

这通常不是关于异常处理的问题,但它特别适用于某些框架的使用.典型起点的几个例子:

  • GWT:接口的public void onFailure(Throwable caught)实现AsyncCallback.
  • JAX-RS:接口的public Response toResponse(E throwable)实现ExceptionMapper<E extends Throwable>.

上述两种方法都接收一个实例Throwable.通常,我看到开发人员使用一个简单的"if/else if"块来区分处理逻辑:

// As specified by the AsyncCallback class of the GWT framework
public void onFailure(Throwable caught) {
    if (caught instanceof AnException) {
        // handle AnException
    } else if (caught instanceof AnotherException) {
        // handle AnotherException
    } else if (caught instanceof YetAnotherException) {
        // handle YetAnotherException
    } else if (caught instanceof ...) {
        // and so on...
    }
}
Run Code Online (Sandbox Code Playgroud)

由于我不是"if/else if"块的粉丝,出于多种原因,我想出了以下"模式",它将"if/else if"块转换为"try/catch"块,表现得好像它是一个"开关"块:

public void onFailure(Throwable caught) {
    try {
        throw caught;
    } catch(AnException e1) {
        // handle AnException
    } catch(AnotherException e2) {
        // handle AnotherException
    } catch(YetAnotherException e3) {
        // handle YetAnotherException
    } catch(...) {
        // and so on...
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:是否有任何缺点 - 在性能,最佳实践,代码可读性,一般安全性,或者我没有考虑或注意到的任何其他方面 - 使用这种方法?

Mik*_*bel 5

在正常情况下使用异常来引导程序流程是一种代码味道,但这并不是您在这里所做的。我认为您可以摆脱这种情况,原因如下:

  1. 我们已经出于各种原因捕获并重新抛出异常(例如,“捕获、采取一些操作、传播”)。这在意图上有点不同,但就成本而言并不差。

  2. 您已经承担了至少抛出一次此异常的成本。您可能已经承担了其原因被抛出、捕获、包裹或重新抛出的成本。填充堆栈跟踪的成本已经支付。再次重新抛出已经填充的异常不会增加复杂性。

  3. 您没有使用异常来引导正常代码路径的流程。您正在对错误做出反应,因此您已经走在了特殊的道路上,并且您应该很少(如果有的话)最终到达这里。如果这种模式效率低下,那么除非您遇到大量异常,否则它几乎没有什么关系,在这种情况下您会遇到更大的问题。花时间优化您期望采用的路径,而不是优化您不采用的路径。

  4. 从美学角度来看,没有什么东西像长长的if/else if块链一样让我起鸡皮疙瘩,尤其是当条件仅仅是类型检查时。在我看来,您提出的建议更具可读性。具有多个有序catch子句很常见,因此结构大多是熟悉的。序言try { throw e; }可能不太正统,但很容易推理。

只是传播时要小心Throwable。有些错误,比如VirtualMachineError层次结构,是出现严重错误的迹象,应该允许它们顺其自然。其他的,例如,传达有关原始InterruptedException线程状态的信息,并且它们不应该在不同的线程上盲目传播。有些,例如,跨越这两个类别。ThreadDeath