Java 8 & 9 in Action, Second Edition
Java 8 & 9 in Action, Second Edition
MEAP Edition
Manning Early Access Program
Java 8 & 9 in Action
Second Edition
Lambda, streams, functional and reactive programming
Version 4
Copyright 2017 Manning Publications
For more information on this and other Manning titles go to
www.manning.com
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
welcome
Thank you for purchasing the MEAP for Java 8 & 9 in Action – the second edition of our book
Java 8 in Action.
The past 2½ years have been a real adventure for us! Our aim in writing the first edition of
Java 8 in Action was to contribute to the Java ecosystem by inspiring developers to enjoy and
to exploit the new functional ways of Java programming. We are hugely gratified, and a little
humbled, that our book has been bought by over 20,000 readers around the world.
However, since we wrote the book, further advances have been made to the Java
ecosystem – which we want to share with you in our second edition. These centre around the
forthcoming (June 2017) release of Java 9. Java 9 brings several new features including a
module system and various enhancements to the core Java-8 API additions (Streams,
Collectors, Optional and CompletableFuture). But more importantly, just as Java 8 provided a
shift in thinking by augmenting traditional object-oriented with functional-programming
features, Java 9 provides additional ways of thinking by further augmenting Java 8 with
reactive-programming features based around ‘reactive streams’. So, what are these? Perhaps
the simplest explanation is they provide a computational model of building blocks which run
independently and send messages to one another this ‘reactive programming’ is becoming hot
in the Java community. Yes, this sounds both mysterious and exciting, but that's all we can
say here!
The second edition will incorporate new chapters discussing Java 9 enhancements including
the module system and API additions, further material on designing with functional features
and how you can benefit from the reactive-programming movement. Just as our first edition
did for Java 8, we discuss – in practical ways – how you can benefit from the new features in
Java 9.
In offering this MEAP, we hope not only to excite you about Java 9, but also to benefit from
your feedback on how we can make this second edition better (including it becoming the most
relevant and useful to your personal development as a Java programmer).
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
brief contents
PART 1: FUNDAMENTALS
1 Java 8 & 9: why you should care?
2 Passing code with behavior parameterization
3 Lambda expressions
PART 2: FUNCTIONAL-STYLE DATA PROCESSING WITH STREAMS
4 Introducing streams
5 Working with streams
6 Collecting data with streams
7 Parallel data processing and performance
PART 3: EFFECTIVE PROGRAMMING WITH LAMBDAS
8 Collection API Enhancements
9 Testing and debugging with lambdas
10 Default methods
11 Designing a DSL with lambdas
PART 4: EVERYDAY JAVA
12 Working with collections in Java 8 and 9
13 Using Optional as a better alternative to null
14 New Date and Time API
15 Java Module System
PART 5: REACTIVE PROGRAMMING
16 Enhanced Java Concurrency: CompletableFuture and Reactive Programming
17 CompletableFuture: composable asynchronous programming
18 Reactive programming
PART 6: FUNCTIONAL PROGRAMMING
19 Thinking functionally
20 Functional programming techniques
21 Practical Design with functional programming
22 Conclusions and where next for Java
1
2
Passing code with behavior
parameterization
A well-known problem in software engineering is that no matter what you do, user
requirements will change. For example, imagine an application to help a farmer understand his
inventory. The farmer might want a functionality to find all green apples in his inventory. But
the next day he might tell you, “Actually I also want to find all apples heavier than 150 g.”
Two days later, the farmer comes back and adds, “It would be really nice if I could find all
apples that are green and heavier than 150 g.” How can you cope with these changing
requirements? Ideally you’d like to minimize your engineering effort. In addition, similar new
functionalities ought to be straightforward to implement and maintainable in the long term.
Behavior parameterization is a software development pattern that lets you handle frequent
requirement changes. In a nutshell, it means taking a block of code and making it available
without executing it. This block of code can be called later by other parts of your programs,
which means that you can defer the execution of that block of code. For instance, you could
pass the block of code as an argument to another method that will execute it later. As a
result, the method’s behavior is parameterized based on that block of code. For example, if
you process a collection, you may want to write a method that
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
2
This is what behavior parameterization refers to. Here’s an analogy: your roommate knows
how to drive to the supermarket and back home. So you can tell him to buy a list of things
such as bread, cheese, and wine. This is equivalent to calling a method goAndBuy with a list of
products as argument. But one day you’re at the office and you need him to do something
he’s never done before: pick up a package from the post office. You now need to pass him a
list of instructions: go to the post office, use this reference number, talk to the manager, and
pick up the parcel. You could pass him the list of instructions by email, and when he receives
it, he can go ahead and follow the instructions. You’ve now done something a bit more
advanced that’s equivalent to a method: go, which can take different new behaviors as
arguments and execute them.
We start the chapter by walking you through an example of how you can evolve your code
to be more flexible for changing requirements. Building on this knowledge, we show how to
use behavior parameterization for several real-world examples. For example, you may have
already used the behavior parameterization pattern using existing classes and interfaces in the
Java API to sort a List, to filter names of files, or to tell a Thread to execute a block of code
or even perform GUI event handling. You’ll soon realize that using this pattern is verbose in
Java at the moment. Lambda expressions in Java 8 tackle the problem of verbosity. We show
in chapter 3 how to construct lambda expressions, where to use them, and how you can make
your code more concise by adopting them.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
3
The highlighted line shows the condition required to select green apples. You can assume that
you have a Color enum with a predefined set of colours such as GREEN available. But now the
farmer changes his mind and wants to also filter red apples. What can you do? A naïve
solution would be to duplicate your method, rename it as filterRedApples, and change the
if condition to match red apples. Nonetheless, this approach doesn’t cope well with changes if
the farmer wants multiple colors: light green, dark red, yellow, and so on. A good principle is
this: after writing similar code, try to abstract.
You can now make the farmer happy and call your method as follows:
List<Apple> greenApples = filterApplesByColor(inventory, GREEN);
List<Apple> redApples = filterApplesByColor(inventory, RED);
…
Too easy, right? Let’s complicate the example a bit. The farmer comes back to you and says,
“It would be really cool to differentiate between light apples and heavy apples. Heavy apples
typically have a weight greater than 150 g.”
Wearing your software engineering hat, you realize in advance that the farmer may want
to vary the weight, so you create the following method to cope with various weights through
an additional parameter:
public static List<Apple> filterApplesByWeight(List<Apple> inventory,
int weight) {
List<Apple> result = new ArrayList<>();
For (Apple apple: inventory){
if ( apple.getWeight() > weight ) {
result.add(apple);
}
}
return result;
}
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
4
This is a good solution, but notice how you have to duplicate most of the implementation for
traversing the inventory and applying the filtering criteria on each apple. This is somewhat
disappointing because it breaks the DRY (don’t repeat yourself) principle of software
engineering. What if you want to alter the filter traversing to enhance performance? You now
have to modify the implementation of all of your methods instead of a single one. This is
expensive from an engineering effort perspective.
You could combine the color and weight into one method called filter. But then you’d still
need a way to differentiate what attribute you want to filter on. You could add a flag to
differentiate between color and weight queries. (But never do this! We’ll explain why shortly.)
2.1.3 Third attempt: filtering with every attribute you can think of
Our ugly attempt of merging all attributes appears as follows:
public static List<Apple> filterApples(List<Apple> inventory, Color color,
int weight, boolean flag) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
if ( (flag && apple.getColor().equals(color)) ||
(!flag && apple.getWeight() > weight) ){ ❶
result.add(apple);
}
}
return result;
}
This solution is extremely bad. First, the client code looks terrible. What do true and false
mean? In addition, this solution doesn’t cope well with changing requirements. What if the
farmer asks you to filter with different attributes of an apple, for example, its size, its shape,
its origin, and so on? Furthermore, what if the farmer asks you for more complicated queries
that combine attributes, such as green apples that are also heavy? You’d either have multiple
duplicated filter methods or one giant, very complex method. So far you’ve parameterized
the filterApples method with values such as a String, an Integer, an enum type or a
boolean. This can be fine for certain well-defined problems. But in this case what you need is
a better way to tell your filterApples method the selection criteria for apples. In the next
section we describe how to make use of behavior parameterization to attain that flexibility.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
5
You can see these criteria as different behaviors for the filter method. What you just did is
related to the strategy design pattern, 1 which lets you define a family of algorithms,
encapsulate each algorithm (called a strategy), and select an algorithm at run-time. In this
case the family of algorithms is ApplePredicate and the different strategies are
AppleHeavyWeightPredicate and AppleGreenColorPredicate.
1
See http://en.wikipedia.org/wiki/Strategy_pattern.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
6
But how can you make use of the different implementations of ApplePredicate? You need
your filterApples method to accept ApplePredicate objects to test a condition on an
Apple. This is what behavior parameterization means: the ability to tell a method to take
multiple behaviors (or strategies) as parameters and use them internally to accomplish
different behaviors.
To achieve this in the running example, you add a parameter to the filterApples method
to take an ApplePredicate object. This has a great software engineering benefit: you can
now separate the logic of iterating the collection inside the filterApples method with the
behavior you want to apply to each element of the collection (in this case a predicate).
PASSING CODE/BEHAVIOR
It’s worth pausing for a moment for a small celebration. This code is much more flexible than
our first attempt, while at the same time it’s easy to read and to use! You can now create
different ApplePredicate objects and pass them to the filterApples method. Free flexibility!
For example, if the farmer asks you to find all red apples that are heavier than 150 g, all you
need to do is create a class that implements the ApplePredicate accordingly. Your code is
now flexible enough for any change of requirements involving the attributes of Apple:
public class AppleRedAndHeavyPredicate implements ApplePredicate {
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
7
You’ve achieved something really cool: the behavior of the filterApples method depends on
the code you pass to it via the ApplePredicate object. In other words, you’ve parameterized
the behavior of the filterApples method!
Note that in the previous example, the only code that really matters is the implementation
of the test method, as illustrated in figure 2.2; this is what defines the new behaviors for the
filterApples method. Unfortunately, because the filterApples method can only take
objects, you have to wrap that code inside an ApplePredicate object. What you’re doing is
similar to “passing code” inline, because you’re passing a boolean expression through an
object that implements the test method. You’ll see in section 2.3 (and in more detail in
chapter 3) that by using lambdas, you’ll be able to directly pass the expression
RED.equals(apple.getColor()) && apple.getWeight() > 150 to the filterApples method
without having to define multiple ApplePredicate classes and thus removing unnecessary
verbosity.
Figure 2.2 Parameterizing the behavior of filterApples and passing different filter strategies
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
8
collection. As a consequence, you can reuse the same method and give it different behaviors
to achieve different things, as illustrated in figure 2.3. This is why behavior parameterization is
a useful concept you should have in your toolset for creating flexible APIs.
Figure 2.3 Parameterizing the behavior of filterApples and passing different filter strategies
To make sure you feel comfortable with the idea of behavior parameterization, have a go at
Quiz 2.1!
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
9
Answer:
First, you need a way to represent a behavior that takes an Apple and returns a formatted String result. You did
something similar when you created an ApplePredicate interface:
You can now represent multiple formatting behaviors by implementing the Apple-Formatter interface:
Finally, you need to tell your prettyPrintApple method to take AppleFormatter objects and use them
internally. You can do this by adding a parameter to prettyPrintApple:
Bingo! You’re now able to pass multiple behaviors to your prettyPrintApple method. You do this by instantiating
implementations of AppleFormatter and giving them as arguments to prettyPrintApple:
prettyPrintApple(inventory, new AppleFancyFormatter());
This will produce an output along the lines of
Or try this:
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
10
An apple of 80g
An apple of 155g
…
You’ve seen that you can abstract over behavior and make your code adapt to requirement
changes, but the process is verbose because you need to declare multiple classes that you
instantiate only once. Let’s see how to improve that.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
11
}
return result;
}
}
This is unnecessary overhead; can you do better? Java has a mechanism called anonymous
classes, which let you declare and instantiate a class at the same time. They enable you to
improve your code one step further by making it a little more concise. But they’re not entirely
satisfactory. Section 2.3.3 shows a short preview of how lambda expressions can make your
code more readable before we discuss them in detail in the next chapter.
Anonymous classes are often used in the context of GUI applications to create event-handler
objects. We don’t want to bring back painful memories of Swing but this is a very common
pattern that you see in practice (here using the JavaFX API, a modern UI platform for Java):
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
System.out.println("Woooo a click!!");
}
});
But anonymous classes are still not good enough. First, they tend to be very bulky because
they take a lot of space, as shown in the highlighted code here using the same two examples
used previously:
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
12
Second, many programmers find them confusing to use. For example, Quiz 2.2 shows a classic
Java puzzler that catches most programmers off guard! Try your hand at it.
Answer:
The answer is 5, because this refers to the enclosing Runnable, not the enclosing class MeaningOfThis.
Verbosity in general is bad; it discourages the use of a language feature because it takes a
long time to write and maintain verbose code, and it’s not pleasant to read! Good code should
be easy to comprehend at a glance. Even though anonymous classes somewhat tackle the
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
13
verbosity associated with declaring multiple concrete classes for an interface, they’re still
unsatisfactory. In the context of passing a simple piece of code (for example, a boolean
expression representing a selection criterion), you still have to create an object and explicitly
implement a method to define a new behavior (for example, the method test for Predicate
or the method handle for EventHandler).
Ideally we’d like to encourage programmers to use the behavior parameterization pattern,
because as you’ve just seen, it makes your code more adaptive to requirement changes. In
chapter 3 you’ll see that the Java 8 language designers solved this problem by introducing
lambda expressions, a more concise way to pass code. Enough suspense; here’s a short
preview of how lambda expressions can help you in your quest for clean code.
You have to admit this code looks a lot cleaner than our previous attempts! It’s great because
it’s starting to look a lot closer to the problem statement. We’ve now tackled the verbosity
issue. Figure 2.4 summarizes our journey so far.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
14
You can now use the method filter with a List of bananas, oranges, Integers, or Strings!
Here’s an example, using lambda expressions:
List<Apple> redApples =
filter(inventory, (Apple apple) -> RED.equals(apple.getColor()));
List<String> evenNumbers =
filter(numbers, (Integer i) -> i % 2 == 0);
Isn’t it cool? You’ve managed to find the sweet spot between flexibility and conciseness, which
wasn’t possible prior to Java 8!
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
15
wants you to sort the apples by color. Sound familiar? Yes, you need a way to represent and
use different sorting behaviors to easily adapt to changing requirements.
In Java 8, a List comes with a sort method (you could also use Collections .sort).
The behavior of sort can be parameterized using a java.util.Comparator object, which has
the following interface:
// java.util.Comparator
public interface Comparator<T> {
int compare(T o1, T o2);
}
You can therefore create different behaviors for the sort method by creating an ad hoc
implementation of Comparator. For example, you can use it to sort the inventory by increasing
weight using an anonymous class:
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
});
If the farmer changes his mind about how to sort apples, you can create an ad hoc
Comparator to match the new requirement and pass it to the sort method! The internal
details of how to sort are abstracted away. With a lambda expression it would look like this:
inventory.sort(
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
Again, don’t worry about this new syntax for now; the next chapter covers in detail how to
write and use lambda expressions.
You can use this interface to create threads with different behaviors as follows:
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("Hello world");
}
});
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
16
You can use it as follows by submitting a task to an executor service. Here you will just return
the name of the Thread that is responsible for executing the task:
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> threadName = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
});
Here, the behavior of the setOnAction method is parameterized with EventHandler objects.
With a lambda expression it would look like this:
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action
17
2.5 Summary
Following are the key concepts you should take away from this chapter:
The Java API contains many methods that can be parameterized with different behaviors,
which include sorting, threads, and GUI handling.
©Manning Publications Co. We welcome reader comments about anything in the manuscript - other than typos and
other simple mistakes. These will be cleaned up during production of the book by copyeditors and proofreaders.
https://forums.manning.com/forums/java-8-and-9-in-action