JavaOne 2017 - Java 8, Three Years Ago. Functional & Reactive Programming
Introduction
This article is part of a series of articles on the 2017 JavaOne Conference in San Francisco, CA. The first article, covering Java 9 and 2017 announcements, can be found here: JavaOne 2017 - Java 9, Jigsaw, Java EE 8 & EE4J.
Before rushing into all the new possibilities offered by the release of Java 9, we would like you to take a break and think about how Java 8 has changed your daily life as a Java developer
This article will also be an opportunity to focus more generally on the concepts of functional programming and immutability, finishing up with another software development paradigm that is gaining popularity in the Java world: reactive programming.
Java 8 : The Good, the Bad and The Ugly
While Java 9 was clearly the main attraction, the conference was also a good opportunity to gather feedback and experiences on Java 8 features introduced three years ago. Among these, streams and lambdas were a major evolution, and a paradigm shift from Java 7.
Several talks focused on these two features as they required adaptation by developers, and presented good practices for applying new functional programming principles. One of the recurrent themes among talks was that with the new features of Java 8, there were many good things that could easily be misused or overused. Several talks covered common pitfalls encountered when using these features.
If we had to focus on one particular talk that gave a good overview of good practices for Java 8, it would be the “Java 8, The Good, The Bad And The Ugly” talk, by Brian Vermeer.
Working With Streams
A stream is not a data structure, not storage. Streams are a tool used to create a flux of data on which operations can be applied: transformations, filter, groupings.
– Brian Vermeer
While streams are a very powerful tool, one thing should be kept in mind: “With great power comes great responsibility”.
And three years later, it is still a daily challenge for developers. Before thinking about the “how”, it has become necessary to think about the “when” and “why”.
Developers should resist the temptation to use Streams everywhere when they are not suited.
– Brian Vermeer
And not using Streams everywhere is not enough. They should also be used in a smart way. So what is a “smart way” of using Streams?
The good news is, all you need to do is to keep it simple and write as little code as possible - the famous “KISS” principle (Keep It Simple, Stupid), which isn’t applied as often as it should. Thinking “simple” is paradoxically not always simple! On this topic, you can refer to the following article, rightly named Use Stream Api simpler or don’t use it at all
Advanced Use
By using concepts and processes underlying streams, it becomes possible to extend the JDK Stream API to address unmet needs.
For example, José Paumard demonstrated in a live coding session Streams In The Wild how he used the Spliterator API to develop a helper library that can be used just as existing features in the JDK.
Lambdas
Lambdas were introduced in Java 8 as well. A lambda is an anonymous function (or “lambda abstraction”, hence the name), i.e. a function not bound to an identifier. For example, in Java 7, without lambdas, in order to pass a function as a parameter, anonymous classes must be used, along with extensive boilerplate codes. With the introduction of lambdas in Java 8, this can be done in a much more concise way.
Short And Sweet With Lambdas !
What is more gratifying than being able to refactor one’s code from:
Collections.sort(beers, new Comparator<Beer>() {
@Override
public int compare(Beer b1, Beer b2) {
return b1.getName().compareTo(b2.getName());
}
});
to:
Collections.sort(beers, (b1,b2) -> b1.getName().compareTo(b2.getName()));
Although the conciseness and efficiency of these refactorings can hardly be questioned, they can remain a challenge for a 100% Java developer who has always used OOP. Is this an opportunity for JS developers to join the ranks of Java?
But again, this conciseness and efficiency comes from the developer with his rigor and his mindset, and not in any case from using some random tool.
The Cure? Why, Practice Of Course!
This is what José Paumard demonstrates using live coding Free your Lambdas: how to transform day-to-day, recurrent coding in basic Java applications (such as sorting a list of people) into a Java 8 version using lambdas, in order to improve legibility, robustness and extensibility.
Once the gap has been breached, it even becomes amusing to see that even design classics such as the Factory and Builder patterns can be implemented using lambdas.
And today, the GoF to the rescue. Strong fundamentals here! 😉 pic.twitter.com/DT6Ff1mILj
— Baptiste Meurant (@bmeurant) October 2nd, 2017
Optionals: Are They Good Practice?
Very few talks even mentioned Optionals. Among those we attended and that did Java 8, The Good, The Bad And The Ugly, the only advice given and widely shared was:
Use Optionals only in publicly available code, or not at all!
– Brian Vermeer
Concluding on Java 8
Three years after the introduction of Java 8, the main conclusion might be:
Links To Talks
- Java 8: the Good, the Bad and the Ugly
- Streams in the Wild
- Streams in JDK 8: The Good, the Bad, and the Ugly
- Free Your Lambdas
- Use Stream Api simpler or don’t use it at all
From Java 8 to Functional Programming
Functional Programming
After 20 years of object-oriented programming, Java 8 introduced functional programming, which is a major shift for the Java developers. Two lively, fun-to-watch talks were given on this topic by Venkat Subramaniam: Designing Functional Programs, and Refactoring to Functional Style - one with more theoretical concepts and the other one on how to apply it to existing code.
He explained that although functional programming may seem arduous when one is used to regular OOP, the two should not be opposed to each other, but rather seen as complementary tools. In the end, in a more pragmatic way, the idea is more to adjust one’s coding habits and move towards:
imperative + object-oriented ==> functional + object-oriented
And to do so, you have to ask yourself: what is truly essential and useful among theoretical principles?
Polymorphism is to object oriented programming as function composition & lazy evaluation is to functional programming.
– Venkat Subramaniam
An interesting way to approach this change is organizing a project using two circles:
- one “circle of purity” including “pure” functions (respecting all concepts like immutability, etc.),
- one “ring of impurity” including everything else (for example dealing with exceptions where “pure” concepts cannot be applied).
(Imp)Pure functions:
Pure functions are those functions that don’t have any side effects, and those functions only. So what exactly is a side effect? A function can only depend on its input. Calling a function with the same input always has to produce the same output. No matter how often or at what time you call it.
But the truth is, we often face impure functions and we usually also need them. Examples of impure functions include:
- getting a random number
- getting the current system time
- getting the user input
- reading data from a file
- reading data from a network.
This approach lets you apply very theoretical concepts that are not always easy to respect, and a pragmatic vision. Once again, practice and tips on day-to-day examples show the way for a smooth transition and for reaping the best of both worlds. Refactoring to Functional Style A talk to watch to learn how to refactor:
- practical aspects and benefits of using FP
- pitfalls to watch for
- and most of all, why do so? … All of this while sending subtle messages to the audience on how to take baby steps to refactor, on the importance of knowing APIs well and on proper null pointer handling.
Subliminal message: do not stop here and take one hour to watch his Devoxx talk: 12 ways to make code suck less - his talk at JavaOne wasn’t filmed
Immutability
Immutability is one of the core concepts of functional programming. In his talk on Immutable collections, Paul Sandoz goes over what is offered in Java 8 for immutable collections and their advantages:
- No need to think about concurrency and data races
- Resistant to misbehaving libraries
- Immutable collections are constants that may be optimized at runtime.
Developer’s Wish List
- Manifests immutability (of the collections, not their elements)
- Sealed (not publicly extensible) - no one should be able to extend these (to maintain trust)
- Provide a bridge to mutable collections (not an extension thereof)
- Efficient construction, updates and copying.
JDK Collections as Immutable Collections ?
Unfortunately, as seen in this analysis, it is still necessary to revert to other alternatives such as Guava collections, Closure or Scala.
Concluding on Functional Programming
If all of this wasn’t enough to convince you, please have a look at the following article: Pure bliss with pure functions in Java, which, like Venkat, shows that the idea is not to become a “hardcord functional purist” but rather to become a productive and pragmatic developer by using the best of both worlds.
Once again, these concepts, tools or coding principles will never replace a developer’s expertise, perspective and common sense.
Links to Talks
- Designing Functional Programs
- Refactoring to Functional Style
- Immutable Collections
- immutability & Pure Functions
- Pure bliss with pure functions in Java
Reactive Programming
What Is Reactive Programming ?
Reactive Programming has become very popular of late, and with good reason. Simply put, it’s a non-blocking alternative to traditional programming solutions, working entirely with event driven data streams and functional programming concepts to manipulate these streams.
Of couse, to introduce reactive programming, we could start by going over general concepts, explain the goals, follow with common issues, make a detour and talk about the reactive manifest and use cases… and explain the Reactive Streams initiative and quote:
Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure.
– http://www.reactive-streams.org
Except that to conclude this article, we’ll suggest not to bother with all this, and rather get your hands dirty on some real code from the Spring ecosystem right away for once, to give you an idea of new possibilities offered by the latest release of Spring 5.
We will only present the Web code here (but Josh Long’s live coding session at DevoxxUs 2017 Reactive Spring by Josh Long where he builds a reactive application from scratch is really worth watching, especially if you have never watched a live coding by Josh before - his session at Java One was not recorded).
Just bear in mind that this new version underwent a major refactoring to comply with the reactive approach from end to end (based on Project Reactor, under Pivotal’s governance).
Spring Web Reactive
Spring Framework 5 adds a new spring-web-reactive module that supports the same @Controller programming model as Spring MVC but executed on a reactive, non-blocking engine.
The diagram below shows how Spring MVC and Spring Web Reactive compare side by side:
From Spring Web Mvc to Spring Web Flux
What are the impacts on code?
@RestController
@RequestMapping("/movies")
class MovieRestController {
private final FluxFlixService fluxFlixService;
public MovieRestController(FluxFlixService fluxFlixService){
this.fluxFlixService = fluxFlixService;
}
@GetMapping
public Flux<Movie> all(){
return fluxFlixService.all();
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Mono<Movie> byId(@PathVariable String id) {
return fluxFlixService.byId(id);
}
@GetMapping(value = "/{id}/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<MovieEvent> events(@PathVariable String id){
return fluxFlixService.byId(id)
.flatMapMany(fluxFlixService::streamStreams);
}
}
As you can see in the example above, our habits won’t be too drastically changed, and the major concepts of Sprint MVC are still there. The main change is return types, with the new concepts of Mono and Flux representing a data sequences of 0..1 and 0..N.
We’ll end this article with a flourish and a version that blends functional and reactive aspects, and showcasing new concepts of Router Function and Handler Function:
//Router functions only
@Configuration
class WebConfiguration {
@Bean
RouterFunction<?> routes(FluxFlixService ffs) {
return RouterFunctions
.route(GET("/movies"),
serverRequest -> ServerResponse.ok()
.body(ffs.all(), Movie.class))
.andRoute(GET("/movies/{id}"),
serverRequest -> ServerResponse.ok()
.body(ffs.byId(serverRequest.pathVariable("id")), Movie.class))
.andRoute(GET("/movies/{id}/events"),
serverRequest -> ServerResponse.ok()
.contentType(MediaType.TEXT_EVENT_STREAM)
.body(ffs.events(serverRequest.pathVariable("id")), MovieEvent.class));
}
}
//RouterFunctions + RouterHandler
@Component
class MovieHandler {
private final FluxFlixService ffs;
MovieHandler(FluxFlixService ffs) {
this.ffs = ffs;
}
Mono<ServerResponse> all(ServerRequest serverRequest) {
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(ffs.all(), Movie.class);
}
Mono<ServerResponse> byId(ServerRequest serverRequest) {
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(ffs.byId(serverRequest.pathVariable("id")), Movie.class);
}
Mono<ServerResponse> events(ServerRequest serverRequest) {
return ServerResponse.ok()
.contentType(MediaType.TEXT_EVENT_STREAM)
.body(ffs.events(serverRequest.pathVariable("id")), MovieEvent.class);
}
}
@Configuration
class WebConfiguration {
@Bean
RouterFunction<?> routes(MovieHandler movieHandler) {
return RouterFunctions
.route(GET("/movies"), movieHandler::all)
.andRoute(GET("/movies/{id}"), movieHandler::byId)
.andRoute(GET("/movies/{id}/events"), movieHandler::events);
}
}
Conclusion on Reactive Programming
And again, as a good Spring fanboy, I can only encourage you to check out new features on the Spring side, or better yet to try them out!
Like Pivotal advocates remind it at the beginning of each presentation: using reactive programming everywhere is not a goal in itself, but if you need to use it, then the Spring ecosystem can help.
Links to Talks/Documentations
Conclusion
When the time comes to review and assess what Java 8 has brought to the community, it is clear that it came with many new possibilities and a real enhancement to Java, by opening the door on a lot of new concepts (functional programming, etc.). But the number of attendees on talks about feedback and good practices shows that we need to be mindful and use common sense when using these new possibilities. No matter how powerful are the tools, they will never replace thinking and experience.
The first article, Java 9, Jigsaw, Java EE 8 & EE4J, likely made you want to try out all new features. I (humbly) hope mine will have convinced you of the importance to take a step back and help new developers doing so as well by teaching them to be pragmatic and keep in mind the KISS principle - it is not the tool that is important, but how you use it.
Writing software is hard.
Writing good software is harder.
Writing good, simple software is the hardest.
Writing good, simple software as a team is the hardest… est.
– Van Dongen
As a reminder: