让特定异常绕过广泛捕获块的习语

  • 本文关键字:习语 异常 java exception
  • 更新时间 :
  • 英文 :


通常,在实现模板方法或接口方法时,只能引发该方法定义的一种特定类型的异常。但是,您的实现可能会使类化为 API 的类,从而引发不兼容的异常类型或许多不同的异常类型。

当然,您需要捕获它们并将异常包装到适合已实现方法签名的类型中。假设我们要实现这个接口:

public interface SomeDataGetter {
    public long getSomeData() throws IOException;
}

我们的实现利用了一些其他 API 产品来实现这一点,我们调用的 API 方法可能具有以下签名:

public long loadFromDBOrCache(Object ... params) throws SQLException, IOException, ObjectNotFoundException, RuntimeException, FridayException, NotWeekendException, NumberIs42Exception;

我这样做是为了演示无法按具体类型精确枚举所有可能引发的异常的情况。请注意,IOException是我们被允许从实现中抛出的类型。

现在,我可以在实现这一点时采用懒惰的路线,并包装任何内容以适合我的签名:

@Override
public long getSomeData() throws IOException {
    try {
        return loadFromDB(...);
    } catch (Exception e) {
        throw new IOException(e.getMessage(), e);
    }
}

这显然会将任何异常包装到 IOException(甚至是 IOException(中,并且效果很好。但是我不想包装 IOExceptions,因为我可以在不包装它们的情况下扔掉它们:

@Override
public long getSomeData() throws IOException {
    try {
        return loadFromDB(...);
    } catch (IOException e) {
        throw e;
    } catch (Exception e) {
        throw new IOException(e.getMessage(), e);
    }
}

您可以想象,如果实现中有多个可能的异常,并且实现中允许多个异常,这很快就会变得很麻烦。对于我想通过的每个异常,我都需要一个额外的捕获。

保持可读性的最佳习语是什么(另外,我很懒惰,不想写所有这些额外的捕获(并且仍然避免不必要的异常嵌套?还是让我不打扰,只是包裹一切?

一种方法是创建一个将所有"禁止的"例外包装在一个允许的例外中,同时返回所有允许的解包例外,如下所示:

private static void throwIoException(Exception e)
    throws IOException // <<= Add other "allowed" exceptions here
{
    if (e instanceof IOException) {
        throw (IOException)e;
    }
    ... // <<= Add checks for other "allowed" exceptions here
    throw new IOException(e.getMessage(), e);
}

现在,您可以使用单个catch块,并根据需要进行包装:

try {
    return loadFromDB(...);
} catch (Exception e) {
    throwIoException(e);
}

这样做的一个令人不快的后果是堆栈跟踪在新创建的IOException的顶部显示实用程序方法,但这并不重要,因为真正的例外是包装的,而不是IOException包装器。如果您捕获的异常恰好是 IOException ,则正确的堆栈跟踪应保留在原位。

我认为将所有

异常包装到IOException(或其他检查异常(中的懒惰路线是一种不好的做法。相反,我会考虑将异常包装在运行时异常中,从而绕过捕获或指定要求。例如

@Override
public long getSomeData() throws IOException {
    try {
        return loadFromDB(...);
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

这更好的原因是检查异常具有一定的含义。例如,如果你在代码中捕获了一个ParseException并将其重新抛出,那么IOException你就是在撒谎。作为代码的用户,我也许能够对某些类型的检查异常做一些事情,但是如果您混淆异常的真正原因,则在发生错误时调试代码将更加困难。

一般来说,我认为您应该尽量减少使用检查异常,因为它在整个应用程序中乱扔错误处理代码。此外,如果您使用的是其他人的代码,则无法保证无论如何都不会抛出RuntimeException(除非您仔细阅读所有内容(。因此,无论如何,您都必须考虑这种可能性并在某个地方处理它,以便您的应用程序不会崩溃。例如,未检查异常与检查异常的优点已经在这里和这里的其他地方讨论了很多。

最新更新