Java 8 has revolutionized Java. It’s easily the most significant release of Java in the last 10 years. There are a ton of new features including default methods, method and constructor references, and lambdas, just to name a few.
One of the more interesting features is the new
java.util.stream API, which as the Javadoc states, enables
functional-style operations on streams of elements, such as map-reduce transformations on collections
Combine this new API with lambda expressions and you end up with a terse, yet, powerful syntax that significantly simplifies code through the application of projections.
Take, for example, the ostensibly simple task of filtering a collection. In this case, a simple
Message types, created like so:
1 2 3 4 5
With this collection, I’d like to filter out
Messages with a
delay (3rd constructor parameter) greater than 3,000 seconds.
Previous to Java 8, you could hand jam this sort of logic like so:
1 2 3 4 5
In Java 8, however, this job becomes a lot more concise. Collections now support the
stream method, which converts the underlying data structure into a iterate-able steam of objects and thereby permits a new breed of functional operations that leverage lambda expressions. Most of these operations can be chained as well. These chain-able methods are dubbed intermediate, methods that cannot be chained are denoted as terminal.
Briefly, lambda expressions are a lot like anonymous classes except with a lot less syntax. For example, if you look at the Javadocs for the parameter to a
filter method, you’ll see that it takes a
Predicate type. Yet, you don’t have to implement this interface as you would, say, before Java 8 with an anonymous class. Consequently, the
Predicate lambda expression for filtering all values of
delay greater than 3000 would be:
x is the parameter passed in for each value in the stream and everything to the right of the
-> being the expression evaluated.
Putting this all together in Java 8 yields:
Interestingly, due to some other new features of Java 8, the
forEach’s lambda can be simplified further to:
Because the parameter of the
forEach lambda is simply consumed by the
println, Java 8 now permits you to drop the parameter entirely.
Earlier, I mentioned that streams permit you to chain lambdas – in the case above, the
filter method is an intermediate method, while the
forEach is a terminal method. Other intermediate methods, that are immediately recognizable to functional programmers, are:
reduce, to name a few.
To elaborate, I’d like to find all
Messages that are delayed more than 3,000 seconds and sum up the total delay time. Without functional magic, I could write:
1 2 3 4 5 6
Nevertheless, with Java 8 and a bit of functional-foo, you can achieve a more elegant code construct like so:
Note how I am able to chain the
mapToLong methods, along with a terminal
sum. Incidentally, the
sum method requires a specific map style method that yields a collection of primitive types, such as
Functional style programming as a core language feature is an astoundingly powerful construct. And while a lot of these techniques have been available in various 3rd party libraries like Guava and JVM languages like Scala and Groovy, having these features core to the language will surely reach a wider audience of developers and have the biggest impact to the developmental landscape.
Java 8, without a doubt, drastically changes the Java language for the better.