通常,在实现模板方法或接口方法时,您只能抛出该方法定义的一种特定类型的异常。但是您的实现可能会使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);
}
}
如果实现中存在多个可能的异常,并且允许从实现中获得多个异常,则可以想象这会很快变得麻烦。我需要额外捕获每个我要通过throgh的例外。
保持可读性最好的成语(也是,我很懒,而且不想写所有这些额外的捕获量)并且仍然避免不必要的异常嵌套?或者,我不打扰,只是包装一切?
答案 0 :(得分: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
顶部的实用工具方法,但这并不重要,因为真正的异常是包装的,而不是{{1}包装器。如果您捕获的异常恰好是IOException
,the correct stack trace should remain in place。
答案 1 :(得分:0)
我会考虑将您获得的所有异常包装到IOException
(或其他已检查的异常)中的惰性路由是一种不好的做法。相反,我会考虑在运行时异常中包装异常,从而绕过catch or specify requirement。 E.g。
@Override
public long getSomeData() throws IOException {
try {
return loadFromDB(...);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
这更好的原因是检查的例外具有证书意义。例如,如果您在代码中捕获ParseException
并将其重新抛出为IOException
,那么您就是在撒谎。作为代码的用户,我可能能够对某些类型的已检查异常执行某些操作,但如果混淆了异常的真正原因,则在发生错误时调试代码将更加困难。
一般来说,我认为您应该尽量减少使用已检查的异常,因为它会在整个应用程序中丢失错误处理代码。此外,如果您使用其他人的代码,则无法保证不会抛出RuntimeException(除非您仔细阅读所有内容)。因此,您必须考虑这种可能性并在某处处理它,以便您的应用程序不会崩溃。例如,在其他here和here中已经讨论了未经检查的异常与已检查异常的优点。