Idiom to let a specific exception bypass a broad catch block?
通常,在实现模板方法或接口方法时,只能抛出由该方法定义的一种特定类型的异常。但是,您的实现可能会使类指向抛出不兼容异常类型或许多不同异常类型的API。
当然,您需要捕获它们并将异常包装到适合于实现的方法签名的类型中。假设我们想要实现这个接口:
1 2 3 4 5 |
我们的实现使用其他一些API产品来实现这一点,我们调用的API方法可能具有以下签名:
1 | public long loadFromDBOrCache(Object ... params) throws SQLException, IOException, ObjectNotFoundException, RuntimeException, FridayException, NotWeekendException, NumberIs42Exception; |
我这样做是为了演示这样一种情况,即您不能精确地枚举所有可能由具体类型引发的异常。请注意,IOException是允许我们从实现中抛出的类型。
现在,我可以在实现这一点时采用懒惰的方式,并包装任何适合我签名的内容:
1 2 3 4 5 6 7 8 | @Override public long getSomeData() throws IOException { try { return loadFromDB(...); } catch (Exception e) { throw new IOException(e.getMessage(), e); } } |
很明显,这会将任何异常包装成一个IOException(甚至是IOException),结果正常。但我不想包装IOExceptions,因为允许我不包装就扔:
1 2 3 4 5 6 7 8 9 10 | @Override public long getSomeData() throws IOException { try { return loadFromDB(...); } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException(e.getMessage(), e); } } |
如果实现中存在多个可能的异常,并且允许实现中存在多个异常,您可以想象这会很快变得很麻烦。我需要为每一个我想通过的例外多抓一把。
什么是最好的成语来保持可读性(同样,我很懒惰,不想写所有这些额外的捕获),并且仍然避免不必要的异常嵌套?或者我应该不费心把所有东西都包起来吗?
一种方法是创建一个方法,将所有"禁止"的异常都包装在一个允许的异常中,同时返回所有允许的未包装的异常,如下所示:
1 2 3 4 5 6 7 8 9 | 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); } |
现在您可以使用单个
1 2 3 4 5 |
这样做的一个不愉快的结果是,堆栈跟踪在新创建的
我会认为把你进入
1 2 3 4 5 6 7 8 | @Override public long getSomeData() throws IOException { try { return loadFromDB(...); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } |
之所以这样做更好,是因为选中的异常具有一定的含义。如果您在代码中捕捉到一个
一般来说,我认为您应该尽量减少使用选中的异常,因为它会在整个应用程序中丢弃错误处理代码。另外,如果您使用的是其他人的代码,则无法保证不会抛出RuntimeException(除非您仔细阅读了所有代码)。因此,无论如何,您必须考虑这种可能性,并在某个地方处理它,这样您的应用程序就不会崩溃。例如,未检查异常和检查异常的优点在其他地方和这里已经讨论了很多。