如何以安全可读的方式处理我知道永远不会抛出的IOException?

Eth*_*man 20 java exception-handling exception ioexception

"可能出错的事情与不可能出错的事情之间的主要区别在于,当一件不可能出错的事情出错时,通常会发现无法进入或修复." -道格拉斯·亚当斯

我有一个类FileItems.FileItems构造函数接受一个文件,如果该文件不存在则抛出异常(FileNotFoundException).该类的其他方法也涉及文件操作,因此具有抛出FileNotFoundException的能力.我想找到一个更好的解决方案.一种解决方案,不需要其他程序员处理所有这些极不可能的FileNotFoundExceptions.

事情的事实:

  1. 该文件已被检查存在,但存在极不可能的可能性,通过一些重大的现实错误,在调用此方法之前可能会删除该文件.
  2. 由于1发生的概率非常不同且不可恢复,我宁愿定义一个未经检查的异常.
  3. 该文件已经被发现存在,迫使其他程序员编写代码并捕获已检查的FileNotFoundException,这似乎很乏味且无用.该程序应该在那时完全失败.例如,计算机总是有可能着火,但没有人疯狂到足以迫使其他程序员将其作为检查异常来处理.
  4. 我不时遇到这种异常问题,并且每次遇到这个问题时定义自定义未经检查的异常(我的旧解决方案)都很烦人并且增加了代码膨胀.

代码目前看起来像这样

 public Iterator getFileItemsIterator() {
    try{
        Scanner sc = new Scanner(this.fileWhichIsKnowToExist);
        return new specialFileItemsIterator(sc);        
       } catch (FileNotFoundException e){ //can never happen} 

    return null;
 }
Run Code Online (Sandbox Code Playgroud)

如何在不定义自定义未经检查的FileNotFoundException的情况下更好地完成此操作?有没有办法将checkedException转换为uncheckException?

Hen*_*ing 51

解决这个问题的通常模式是异常链接.您只需在RuntimeException中包装FileNotFoundException:

catch(FileNotFoundException e) {
    throw new RuntimeException(e);
}
Run Code Online (Sandbox Code Playgroud)

此模式不仅适用于在特定情况下(例如您的)不能发生异常,而且当您无法或无意真正处理异常(例如数据库链接失败)时.

编辑:要注意这种看似相似的反模式,我经常在野外看到它:

catch(FileNotFoundException e) {
    throw new RuntimeException(e.getMessage());
}
Run Code Online (Sandbox Code Playgroud)

通过这样做,您可以丢弃原始堆栈跟踪中的所有重要信息,这通常会使问题难以追踪.

另一个编辑:正如ThorbjørnRavnAndersen在他的回答中正确指出的那样,无论是在评论中,还是在更好的情况下,作为异常消息,都可以说明为什么要链接异常.

catch(FileNotFoundException e) {
    throw new RuntimeException(
        "This should never happen, I know this file exists", e);
}
Run Code Online (Sandbox Code Playgroud)

  • 反模式上+1.这让我疯了 (4认同)
  • 我认为反模式是很久以前的延续(1.3?),当时不存在异常链接. (3认同)

Emi*_*l H 11

您可以通过将其嵌入RuntimException中将已检查的异常转换为未选中的异常.如果异常在堆栈中被捕获并使用printStackTrace()输出,则也会显示原始异常的堆栈.

try {
    // code...
}
catch (IOException e) {
    throw new RuntimeException(e);
}
Run Code Online (Sandbox Code Playgroud)

这是一个很好的解决方案,您应该毫不犹豫地在这些情况下使用.


Tho*_*sen 10

考虑使用表单

throw new RuntimeException("This should never happen", e);
Run Code Online (Sandbox Code Playgroud)

代替.这允许您向维护者传达意义,以便在阅读代码时跟随您,但也应该在某些奇怪的情况下抛出异常.

编辑:这也是通过不期望这些异常的机制传递异常的好方法.例如,如果你有一个"从数据库获取更多行"迭代器,Iterator接口不允许抛出例如FileNotFoundException,所以你可以像这样包装它.在使用迭代器的代码中,您可以捕获runtimeexception并使用getCause()检查原始的excpetion.在浏览遗留代码路径时非常有用.


Yis*_*hai 8

如果您的位置不太可能并且应该只是结束程序,请使用现有的运行时异常,甚至RuntimeException本身(如果不是IllegalStateException).

try {
  ....

} catch (FileNotFoundException e) {
    throw new RuntimeException(e);
}
Run Code Online (Sandbox Code Playgroud)