Java Reference
In-Depth Information
This would be a rather imperative implementation. From a functional standpoint, we actually want
three methods. Can you see where? The trick is to realize that walking the directory is a useful trick, and
there is no reason to couple that useful trick to the details about what to do when you get there. One helpful
approach to get to this functional decomposition is to be very strict about your method doing only one thing,
keeping in mind that the average Java developer's concept of “one thing” is probably far too broad: printing
all the lines of all the files in a directory is not one thing; walking the files in a directory is one thing—and
even that is a pretty big thing. It helps if you first do a back-of-the-napkin style of design before you write any
code. If it takes more than one line to describe a function, you're doing something wrong. One functional
decomposition of the problem looks like this:
Given:
[Lines] File file => Stream<Result<String>>
Return all the lines in file
[Walk] File dir => Stream<File>
For each file in dir , return the file in the stream
Implement:
File dir => Stream<Result<String>>
Apply dir to Walk and flatMap the result to Lines
Those are the three functions we will implement now. Since all three of them take a File as the first
argument, we will implement them on a subclass of File , which we will call FunFile . (This class is available
in the also project, should you want to see it in production.)
Let's begin with the Lines method, since it will be the simplest. The java.nio.file.Files class,
introduced in Java 7, has been extended with a number of useful Stream -generating static methods,
including Files.lines . Unfortunately, despite the name, the Files class acts mostly on Path instances.
Furthermore, the API produces a Stream of String objects and throws an IOException . This method
signature makes the method ill-suited for working within streams, but its core functionality is exactly what
we want: we just need to make the API match what we are looking for. The implementation of our wrapper is
given in Listing 4-8. Keep in mind that this is attached to a class extending java.io.File .
Listing 4-8. FunFile.getLines()
/**
* If this file is a plain file, provides the lines of the file (read as
* UTF-8) without the line-termination characters as a stream.
* Otherwise, returns a single {@link Result} instance with an
* {@link IOException}.
*
* @return The lines of the file; never {@code null}
*/
public Stream<Result<String>> getLines() {
if (!isFile()) {
return Stream.of(new Result<>(new IOException(
"File is not a plain file: " + toString()
)));
}
try {
return Files.lines(toPath()).map(Result::new);
} catch (IOException ioe) {
return Stream.of(new Result<>(ioe));
}
}
 
Search WWH ::




Custom Search