Java Reference
In-Depth Information
Printing Out the Results
To implement the functionality to print out the lines, all we need to know is that we are working with a
Stream of WordUsage instances. If we know that we are working with a stream, and we know the definition
of the WordUsage class, then we can implement the next step. We do not need any more details. This kind
of distinction was part of the promise of object-oriented software, and it has really come to pass in this
postfunctional world. The reason it can happen is because the stream provides an abstraction for the flow of
control of the application. Before, Java provided abstractions over data. With streams, you can now pass in the
flow of control itself to a part of the application, and that part of the application decides what to do with it.
In this case, we will be receiving the processed WordUsage instances, and we will be responsible for first
converting the WordUsage instances into a tab-separated value (TSV) output format, and then passing the
resulting stream somewhere to print the output. Printing out the stream of output string instances is easy
enough, so we will leave that to the end. First, let's figure out how to convert the WordUsage instances.
There are two simple approaches we could take, and we'll consider both of them.
If we have the ability to manipulate to the WordUsage class, then we can add that method right onto the
class. The resulting method can then be passed into the stream as a mapping function. The method that we
would add to the WordUsage class would look like this:
public String toTSV() {
return String.format("%s\t%d\t%s\t%d",
textName, lineOffset, word, wordOffset);
}
Given a stream of WordUsage instances, we would then just pass in WordUsage::toTSV as a mapping
function. This will instruct the stream to apply the instance method to each element of the stream. If the
stream of WordUsage instances was named source , then the mapping call would look like this:
source.map(WordUsage::toTSV)
That is certainly the easiest way to do it, and it keeps the implementation details of how to convert
a WordUsage into a TSV within the WordUsage class itself. This will help prevent “code rot,” where code
becomes less functional over time. One of the major causes of code rot is when a calling code site makes
assumptions about the object being called. Those assumptions may hold in the present moment, but if the
class changes, it may not be obvious you need to change the code. To see how this plays out, let's consider
our alternative approach: using a lambda to map a WordUsage to a TSV.
In this case, the code that became our toTSV() method would instead be defined inline. This approach
is commonly the default for non-functional programmers coming into a postfunctional language, and also
for Java developers who have recently discovered and become enamored with lambdas. If the source stream
of WordUsage instances was called source , then the inline lambdas would look like this:
source.map(wordUsage ->
String.format("%s\t%d\t%s\t%d",
wordUsage.getTextName(), wordUsage.getLineOffset(),
wordUsage.getWord(), wordUsage.getWordOffset()
)
);
This would work just fine for the moment. It is less readable than the method version, because you do
not get to use the semantic toTSV method name. If you want to figure out that the output value is TSV, the
developer has to be able to read the format string and then recognize that is TSV. So the method approach
has that advantage. But there is an even bigger (yet more subtle) problem with this approach.
 
Search WWH ::




Custom Search