Cool Java 8 Features: To Use or not to Use
Dr. Nils Göde
Java 8 has brought us a bunch of new language features, among which are Lambda Expressions and the Stream API. The intention of these features is to provide us with a way of expressing frequently used functionality in a cleaner and more concise way. While there are certainly many situations in which these features excel, their usage does not necessarily guarantee that the code is cleaner and easier to read compared to writing it the »old-style way«.
One such questionable situation came up during one of our quality-control projects with one of our customers. Once a month we meet with the developers to discuss the recent improvements and setbacks in the quality of their code. Being generally up to speed with Java and its features, it came at no surprise, that the developers starting using the new features right away. During the latest meeting, one piece of code stimulated a lot of discussion about whether the usage of the new Java 8 features makes the code more readable or not.
Let me first show you the piece of code that we were discussing. I modified the
code a little, such that no customer information is disclosed. The purpose of
the code is to do a certain computation n
times in parallel. Each computation
returns an object of type Result
, which is stored wrapped in a Future
object
and stored in a list. Of course the code does not »make any sense«
without knowing the actual computation. However, it is still useful to
illustrate the problem. Now, here is the version that we found in the customer’s
code, which uses Java 8 features (executor
is of type
java.util.concurrent.ExecutorService
):
As you can see the code uses the new Stream API to obtain the integers, a lambda
expression to create the Future
objects from the integers, another lambda
expression to implement the Callable
parameter passed to executor.submit
and
a Collector to finally create a list from the stream. Now compare this to a
version that does not use Java 8 features:
Now the important question: Which of the two is more readable? More
maintainable? Easier to change? Less error-prone? The first version is obviously
shorter. However, it uses method chaining and nested lambda expressions both of
which complicate the understanding. The second version is dominated by the
circumstantial in-place implementation of the Callable
. Another weakness is
the final variable j
, which is needed to use the value of i
in the call
method. Apart from that the version uses only basic features. In particular the
for
loop is probably closer to what one would expect for iterating through a
sequence of integers.
After thinking some time about both versions, I found the best version to be a
combination of both. The following version uses the traditional for
loop to do
the iteration, but replaces the circumstantial implementation of the Callable
with a lambda expression. The only imperfection that remains is the need for the
variable j
.
After all, I am sure that it will be hard to find a general agreement on whether code with lambda expressions is more readable than old-style Java code. It does certainly depend on the specific code snippet. Furthermore, each of us has his own idea about which type of code is more readable. However, one thing is certain: Some developers will use the new features, others will not and yet others will use them from time to time. The problem is a growing inconsistency of how similar problems are solved in the code. Sooner or later, developers will come across code that does not match their idea of readable code and will have problems understanding and changing it. The larger the number of available features, the higher the inconsistency and heterogeneity of the code.
To mitigate this problem, you should first make sure that each developer knows the range of available features, how they can be used to solve recurring problems and whether they have any implications. Even if they do not use the features themselves, the developers have to know how they work to be able to understand code written by others. You should also include information about when and how to use certain language features in your coding guidelines. This helps to ensure that recurring problems are solved consistently. Finally, you should use a static analysis tool like Teamscale to continuously check for violations of the guidelines and anti-patterns in the usage of language features.

Want to read more articles like this?
Subscribe to our newsletter and you'll be notified when new blog posts arrive.