Java Reference
In-Depth Information
Before we leave the getLines() method, it is worth taking a sidebar to discuss the error condition
handling in this method. If the underlying file is not a “plain file” (e.g., it is a directory), we return an
IOException in the stream. The API requires that the file is a plain file, and so it not being a plain file is a
violation of the API. The general rule is that checked exceptions are used for errors caused by changes in the
execution environment, and unchecked exceptions are used for API violations. If a really good programmer
wouldn't see the exception, it is an unchecked exception; if the exception occurs because of something
beyond the control of the program, it's a checked exception. In this case, it's arguably appropriate to use
an unchecked exception, because it is a violation of the API. However, it is also possible that the file is not a
plain file because it no longer exists. It might also return false because Java couldn't determine the file type
from the file system for some reason, which can happen with NFS. As a result, we are going with the standard
checked exception error approach, even if it is an API violation.
Now that we have the getLines() method, we need the Walk method to walk the tree. This reveals a
slight ambiguity in our API definition: when we say “all the files in the directory,” do we mean “all the files
that are immediate children of the directory,” or do we mean “all the files that are children of the directory,
regardless of intervening directory depth”? In other words, is this a recursive listing or a simple listing? We
can code it easily enough to support both approaches by taking an extra parameter: if true , we will recourse
into subdirectories; if false , we won't.
The implementation of the Walk method is very straightforward, and given in Listing 4-9. We use Files.
list(Path) to implement the walk, and then map the results into a FunFile using the new FunFile(Path)
constructor. The bulk of the method (in terms of lines of code) is spent implementing the recursion: in
addition to handling directories and files distinctly, we also want to ensure that we don't end up in an
endless loop, so we have to check the canonical path.
Listing 4-9. The FunFile.listFiles(boolean) Method
* Returns a stream of the plain files in the directory, recursing into
* subdirectories if the {@code recurse} argument is {@code true}.
* @param recurse Whether to recurse into subdirectories
* @return The plain files in the directory; never {@code null}
public Stream<Result<FunFile>> listFiles(boolean recurse) {
if (!isDirectory()) {
return Stream.of(new Result<>(new IOException(
"File is not a directory: " + toString()
try {
final String thisPath = this.getCanonicalPath();
return Files.list(toPath()).map(FunFile::new).flatMap(file -> {
if (recurse && file.isDirectory()) {
final String filePath;
try {
filePath = file.getCanonicalPath();
} catch (IOException ioe) {
return Stream.of(new Result<>(ioe));
if (!thisPath.equals(filePath)) {
return file.listFiles(true);
} else {
return Stream.empty();
Search WWH ::

Custom Search