Java Path Streams Trap

Categories: Java

Java1.8 streams and closures can be cool:

  return Files.list(baseDir)
            .filter(path -> path.getFileName().toString().endsWith(suffix))
            .collect(Collectors.toList());

However there is a nasty bug in the above: Files.list(baseDir) returns a stream object that must be explicitly closed. Not doing so leaks a file-handle to the directory being listed! Sadly, the bugfree version does not look quite so elegant:

  try(Stream<Path> paths = Files.list(baseDir)) {
    return paths
            .filter(path -> path.getFileName().toString().endsWith(suffix))
            .collect(Collectors.toList());
  }

And by the way, the “toString” is necessary too, as Path.endsWith() and String.endsWith() are not the same thing.

Designing good library APIs is an art - and IMO whoever at Sun approved Files.list and Path.endsWith screwed up. If only they had read Chapter 5, “Candy Machine Interfaces” from “Writing Solid Code” by Steve Maguire - an old book now, but that chapter has some great and still-relevant advice.

BTW, it seems many other people have noticed this too. And in fact one of the designers of the stream framework defends the choice here. Regardless of whether the fix is for streams to be closed automatically, or to avoid methods returning must-close streams (eg provide Files.foreach(path, closure) instead), what is currently provided is just too easy to get wrong..