异常处理的“切换”等价物

时间:2017-09-18 13:43:14

标签: 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...
    }
}

由于我不是“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...
    }
}

我的问题是:在性能,最佳做法,代码可读性,一般安全性或其他任何我不考虑或注意的事项方面是否有任何缺点 - 使用这种方法? < / p>

3 个答案:

答案 0 :(得分:4)

使用异常来指导正常环境下的程序流是一种代码味道,但这并不是你在这里所做的。我认为你可以通过以下几个原因来解决这个问题:

  1. 我们已经出于各种原因捕获并重新抛出异常(例如,“捕获,采取某些行动,传播”)。这在意图上有点不同,但在成本方面并没有差别。

  2. 您已经承担了至少一次抛出此异常的费用。您可能已经承担了抛出,抓住,包裹或重新抛出原因的成本。填写堆栈跟踪的成本已经支付。再一次重新抛出已经填充的异常并不会增加复杂性的顺序。

  3. 您没有使用例外来指导普通代码路径的流程。您对错误做出了反应,因此您已经在例外路径上,并且您应该很少(如果有的话)在这里结束。如果这种模式效率低下,除非遇到 lot 异常,否则几乎没有问题,在这种情况下你会遇到更大的问题。花时间优化您期望采取的路径,而不是那些您没有的路径。

  4. 在美学上,当条件仅仅是类型检查时,很少有东西会使我的皮肤像if/else if块的长链一样爬行,尤其是。在我看来,你提出的建议更具可读性。拥有多个有序的catch子句很常见,因此结构主要是熟悉的。 try { throw e; }序言可能是非正统的,但很容易理解。

  5. 宣传Throwable时要小心谨慎。一些错误,例如VirtualMachineError层次结构,表明某些事情已经发生了可怕的错误,应该允许它们运行。其他人,如InterruptedException,会传达有关原始线程状态的信息,不应在不同的线程上盲目传播。有些内容,如ThreadDeath,可以跨越两个类别。

答案 1 :(得分:2)

性能只有在抛出大量错误时才有意义。它不会影响成功案例中的表现。有太多的错误比处理它们的时间要多得多。

如果调用本地方法,并抛出异常,那么使用catch块来处理它就没问题了。这是相同的,但使用远程方法。这不是正常的控制流,因为在获取方法之前已经从RPC调用中抛出异常,因此使用通常的异常处理结构是可以的。

编译器可以执行一些检查,例如检查是否首先列出了最具体的异常,但是使用通用Throwable类型会丢失某些类型的安全性。由于框架,这是不可避免的。

对于这里的任何一个例子我都没问题,因为它没有太大的区别。

答案 2 :(得分:0)

你展示的两个代码块实际上非常相似:乍一看它们与我的眼睛都是相同的“形状”。

值得注意的是if / else链实际上比try / catch版本更少的代码行更容易理解。

我认为try / catch版本本身并不是错误,但是当像这样并排比较时,我没有看到任何理由为什么更好要么。

在其他条件相同的情况下,无争议的代码总是优于有争议的代码:您永远不会希望代码的读者分心,远离 what < / em>您的代码是通过您选择的 来完成的。