When programming in a functional style, it really pays to first stop and think about what you really want
to accomplish. First, let's consider the shape of the function that we want. We know that we want to act on a
java.io.File instance, so we should either take that a file as input or make it as a method of something that
extends the File class. The output side is trickier. We do not know what a user is going to do with the lines
of the file. We could take a Consumer to process them, but it's both simpler and more flexible to return the
lines. We could read all the lines into some kind of Collection , but that requires all the lines to be read into
memory, and it prevents any processing from occurring until the lines are done being read. So it is best to
return a Stream , which provides flexibility and support for concurrent processing. But a stream of what?
The return value may not be as obvious at it initially seems. A Stream of String instances seems like the
obvious choice, but that is only kind of right. At any point in time, we could have an IOException thrown,
and when you are dealing with things like navigating directories, you should absolutely expect them.
Our pattern so far in this chapter has been to create two methods: one that accepts a Consumer to process
the errors, and one that returns Result objects. Although duplication of the API is made simple through
functional programming, it is certainly annoying and it clutters the class. Let's try for a different approach.
The new approach to processing the class will still use the Result class for elements in the Stream , but
we will give our users a new tool. We will provide a class that is constructed with a Consumer , and which
processes a Stream of Result instances to remove the exceptional cases. This gives the user the ability to
specify how to handle exceptions after the fact, and convert the Stream of Result s of String s into the much
more familiar Stream of String s. So now we have two things to write: the method that will take a File and
produce a Stream of Result s of String s, and a post-processor class that will turn a Stream of Result s of
String s into a Stream of String s. Now it is time to start thinking about implementations, starting with the
Complex Stream Processing Using Creative Flattening
This is where we will really get into a fancy Stream stunt, and the real power of a Stream will come out.
The difficulty is that we have a fairly complex operation on a stream. We want to handle and then remove
elements from the stream meeting a certain criteria: in this case, we want to handle and remove Result
instances that contain exceptions.
If you glance over the Stream API, it first seems like we want to perform a map operation: we want to take
in types of Result<String> and return a String , and the map method is how you transform the type if the
Stream . The map method looks like this:
<R> Stream<R> map(Function<? super T,? extends R> mapper)
The problem is that we do not have a one-to-one mapping: not every input will produce an output.
We could return an Option , but what does that gain us? Why wouldn't we just stick with our Result type
instead? If we get fixated with the map method, we could then create a two-part system, first performing
the map and then performing the filter to remove elements. If we were to go down that point, we would
not be leveraging the type system to tell us that the Result has been handled, and we would be creating a
possibility for an error to creep in between when the Result is handled and where it is removed. A better
alternative is to use flatMap .
The flatMap method is based on the concept of “flattening,” which is a functional structure that takes in
a container of containers, and produces a single “flat” container with all the elements of all the containers.
Consider that you had a list that looked like this:
List<List<String>> list = Arrays.asList(
Arrays.asList("Eddard Stark", "Catelyn Tully", "Benjen Stark"),
Arrays.asList("Lysa Telly", "Jon Arryn", "Robin Arryn"),
Arrays.asList("Cersei Lannister", "Robert Baratheon")