java.io.File.listFiles vs java.nio.Files.list及其抛出的IOException

Rol*_*and 3 java nio java-io java-8 java-stream

为什么Files.list抛出IOExceptionFile.listFiles不是?

看一下Files.list(Java 8)的源代码,我更加好奇为什么没有抛出,UncheckedIOException因为它也被抛入迭代器中.

如果我用我替换File.listFiles-code Files.list现在需要处理一个我以前没有真正处理过的异常.毋庸置疑,大多数开发人员甚至不知道他们在那时需要处理什么;-)或者只是放一个// TODO和/或e.printStackTrace()那里.

这使得在Stream这里使用相当麻烦,因为你需要用一个try/catch或重新抛出异常来包围它,这在遗留代码中甚至是不可能的.

那么为什么要做出这个决定呢?

Hol*_*ger 6

首先,开发人员必须始终处理可能发生的错误情况.

  • File.list():

    返回null如果此抽象路径名不表示一个目录,或者发生I/O错误.

  • Files.list(Path):

    抛出:

    • NotDirectoryException - 如果文件无法以其他方式打开,因为它不是目录(可选的特定异常)
    • IOException - 如果在打开目录时发生I/O错误

所以区别在于开发人员可能很容易忘记检查null,只要没有错误的情况就不会注意到.当问题发生在客户并且您的应用程序抛出NullPointerException而不是处理可能微不足道的问题时,您将以困难的方式学习它.

你的陈述" 不用说,大多数开发人员甚至不知道他们在那时需要处理什么 " ,这说明了这一点.确实,但编译器会在编译时告诉你,你必须处理IOException.与File.list()检查失败的地方不同null.在任何一种情况下,你仍然可能处理得不好,但是没有办法阻止这种情况.

当然,一旦你了解,你必须处理的问题,你可能会问,怎么你要处理它,这取决于这类问题.返回值null不会告诉您有关问题的任何信息.您可以检查"非目录"条件File.isDirectory()并希望它之间没有更改,但如果文件目录,则您没有任何提示从何处开始.

相反,抛出IOException不仅允许您区分NotDirectoryException和其他错误条件,IOException是特定异常的森林的基类,可以精确地描述问题,例如AccessDeniedException.即使异常具有非特定类型,它也可能具有有意义的消息,您可以向用户呈现.

请注意,这是使用这两个API的一般模式:

  • File.renameTo(File)

    返回:

    true当且仅当重命名成功; false除此以外

  • File.delete()

    返回:

    true当且仅当文件或目录被成功删除; false除此以外

那么当这些方法中的任何一个返回时你会怎么做false

  • Files.move(Path,Path,CopyOption...)

    抛出:

    ...

    • FileAlreadyExistsException- 如果目标文件存在但由于REPLACE_EXISTING未指定选项而无法替换(可选的特定异常)
    • DirectoryNotEmptyException- REPLACE_EXISTING指定了该选项但该文件无法替换,因为它是非空目录(可选特定异常) AtomicMoveNotSupportedException- 如果options数组包含该ATOMIC_MOVE选项但该文件不能作为原子文件系统操作移动.
    • IOException - 如果发生I/O错误

这就是我所说的有用的......

  • Files.delete(Path)
    抛出:
    • NoSuchFileException - 如果文件不存在(可选的特定异常)
    • DirectoryNotEmptyException - 如果文件是目录,否则无法删除,因为该目录不为空(可选的特定异常)
    • IOException - 如果发生I/O错误

再一次,比一个更有帮助boolean.但请注意,boolean deleteIfExists(Path)如果您想要非特殊地处理这一个微不足道的情况,也会有这种情况.由于所有其他非平凡条件仍然作为例外处理,因此您不能混淆它们.

当然,API设计人员可能会使用未经检查的异常,但这会导致什么?

这使得Stream在这里的使用相当麻烦,因为你需要用try/catch包围它或者重新抛出异常,这在遗留代码中甚至是不可能的.

究竟.您可以更改从不抛出此类异常的代码(因为在错误的情况下File.list()返回null)来调用可能抛出未经检查的异常的新方法,因为这"可能在遗留代码中" - 但是旧调用者不期望该异常并且永远不会处理它.

捕获异常并且在null返回之前的行为与之前完全相同(如果您曾在旧代码中检查过),可能确实很麻烦,但这不是处理此类情况的预期方法,那么为什么这样做会让人感到舒服......