From Guava's FluentIterable via StreamSupport to Java 8 Streams
18 Aug 2015
If you’re programming in Java, you probably noticed the recent move towards streams, lambdas and a more functional style of writing code, greatly facilitated by Java 8’s stream API and anonymous function syntax. Great if you’re doing a clean new project on Java 8, but what about existing codebases?
Our team is currently on Java 7, but looking to move towards Java 8 in the course of the next year. To get into the habit of things, we’ve started using the concepts of functional programming in Java 7. We have been using Guava’s FluentIterable for this, which I would heartily recommend to anyone under similar circumstances.
However. The Guava API is somewhat lacking in some parts, with the team taking the stand they will not improve the API because Java 8 is the more logical upgrade path. Meanwhile, each call to the Guava libraries we add to our code will cost us time later on when we want to remove the dependency. So teams that are going to migrate to Java 8 (like us!) don’t have it in their best interest to keep using Guava.
What’s the alternative? We’re currently experimenting with using StreamSupport, the backported version of the Java 8 stream API to Java 6 and 7.
The rest of this post will explain our findings, and some strategies to help the migration. I will keep this post updated when I uncover new information. Of course, feel free to remark on things I have missed!
#Contents
#The theory, or “Why do we have to jump through all these hoops?”
##Java 8 vs Java 7
First of all, Java 8 brought a lot of improvements that you simply will have to miss in Java 7. This means that code will not be as concise under Java 7. In some cases, you might need completely different workarounds. Please note that I’m not aiming to list all Java 7/8 differences, only those relevant to the streams API.
###Lambdas
The most obvious one is lambdas for functional interfaces:
Much of the boilerplate can be hidden by extracting the anonymous inner class implementation to another class, possibly with its own factory method, which has the bonus advantage of forcing you to name the otherwise anonymous piece of code.
###Method references
This would take a long time to explain in depth, so I will just leave you with an example:
###Generics
Java 8 comes with improvements in type inference for generics related to generic parameters. Note that it seems like the proposed type inference on method chains did not make it. The example below demonstrates the change that was implemented.
Given the following class definition:
Sample usage looks like:
Note that, because the generic type cannot be inferred in Java 7, the method call must be annotated with the type. The syntax for this makes it mandatory to add the class name in front of the method.
In Java 8 however the generic type can be inferred. This opens the option to statically import the method name, which can lead to more readable code, as seen above.
###Default interface methods
Java 8 adds the ability to define default methods on interfaces.
##StreamSupport
###Package differences
StreamSupport lives in its own package java8.util.stream, which resembles the Java 8 package java.util.stream. This makes it possible for the two to co-exist, but it makes removing the StreamSupport dependency a little bit tricky once you switch to Java 8. Often, you will be able to simply remove the 8 from the package name, but this won’t always work. Some examples of this are below.
###Starting a stream
StreamSupport is not entirely able to recreate the Java 8 API, for one simple reason: you can’t change the existing Java 7 implementations of the Java standard library. In particular, and most crucially, the java.util.Collection.stream() method can’t be added to Java 7.
StreamSupport solves this by moving this method to a wrapper method:
Note that this is the same approach as Guava:
If you want to remove the dependency on StreamSupport after introducing Java 8, you will have to change all these calls. We haven’t tried this yet, but I am pretty certain we will be able to solve this problem with IntelliJ’s structural search and replace.
Streamsupport moves these to a java8.util.functions.Predicates class, and makes them wrapper methods.
Probably another job for structural search and replace.
##Wrappers
The Guava concepts of Function and Predicate are, of course, the same concepts as those in Java 8, and thus StreamSupport. While it is preferable to refactor an existing Function/Predicate to the new interface, sometimes there are a few too many usages to immediately change all depending code. It should however be no surprise we can translate from one implementation to the other. We use the following wrappers:
We choose the short name to be minimally intruding:
#The practice, or “How do I…”
##Collect to a list
Note that you need the generic type in the StreamSupport version.
##Collect to SortedSet
Generalizing these solutions to also accept a comparator is an excellent exercise for the reader.
##Combine predicates
#Conclusion
Functional programming in Java 7 is not great, but it is workable. Upgrading to Java 8 should not be a problem, but removing all dependencies and cleaning all the boilerplate code might prove to be a hard job. StreamSupport can help, but you can still expect to have some work.
If you are part of a scrum team, you hopefully know about retrospectives. At the end of each sprint, the team looks back, and thinks about how to improve their way of working. It's an invaluable feedback loop, and even if you are not doing scrum, you should think about incorporating this practice in one way or another.
However, if you are part of a long-running scrum team, you maybe noticed that at some point the retrospectives become less valuable. The same topics come up, but the team cannot define an action to fix them. Maybe actions contradict earlier decisions. Or actions are defined, but not executed - the actions are too hard, or not truly perceived as useful. What can we do?
My team is looking to migrate to Java 8 in the near future. As a first introduction to Java 8 we’re doing a reading group of “Java SE 8 for the Really Impatient” by Cay S. Horstmann. This post is just a quick listing of repositories of colleagues solving the solutions, for future reference and because it’s too long to put in a tweet.