3

通常,在实现模板方法或接口方法时,您只能抛出该方法定义的一种特定类型的异常。但是您的实现可能会为引发不兼容异常类型或许多不同异常类型的 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);
    }
}

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

什么是保持可读性的最佳习语(另外,我很懒,不想写所有这些额外的捕获)并且仍然避免不必要的异常嵌套?或者我不应该打扰,只是把所有东西都包起来?

4

2 回答 2

2

一种方法是制作一种方法,将所有“禁止”异常包装在允许的异常中,同时返回所有允许的未包装异常,如下所示:

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正确的堆栈跟踪应该保留在原地

于 2013-09-27T18:18:58.177 回答
0

我认为将所有异常包装到IOExceptions (或另一个检查的异常)中的惰性路线是一种不好的做法。相反,我会考虑将异常包装在运行时异常中,从而绕过catch 或指定要求。例如

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

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

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

于 2013-09-27T18:23:48.230 回答