In this presentation we introduce Java 8’s main new features and show how you can benefit from them to write code that is more readable and more flexible to requirement changes.
We will show how:
1) Lambda expressions and behaviour parameterisation let you write concise code that can cope for requirement changes
2) The new Streams API lets you express complex data process queries in a succinct way while automatically leveraging your multi-core architecture
3) Using the new Optional class can let you reduce unexpected NullPointer exceptions
4) Default methods bring a form of multi-inheritance to Java
4. Java
8
in
Ac$on:
Lambdas,
Streams
and
func$onal-‐style
programming
4
• Co-‐authored
with
Mario
Fusco
&
Alan
Mycro`
• Most
complete
book
on
Java
8
h"p://manning.com/urma
5. Why
Java
8
Before:
Collec$ons.sort(inventory,
new
Comparator<Apple>()
{
public
int
compare(Apple
a1,
Apple
a2){
return
a1.getWeight().compareTo(a2.getWeight());
}
});
A5er:
inventory.sort(comparing(Apple::getWeight));
5
6. Why
Java
8?
6
List<Dish>
lowCaloricDishes
=
new
ArrayList<>();
for(Dish
d:
dishes){
if(d.getCalories()
<
400){
lowCaloricDishes.add(d);
}
}
List<String>
lowCaloricDishesName
=
new
ArrayList<>();
Collec$ons.sort(lowCaloricDishes
,
new
Comparator<Dish>()
{
public
int
compare(Dish
d1,
Dish
d2){
return
Integer.compare(d1.getCalories(),
d2.getCalories());
}
});
for(Dish
d:
lowCaloricDishes){
lowCaloricDishesName.add(d.getName());
}
6
sor$ng
by
calories
filtering
low
calories
Extract
names
8. Why
Java
8?
(
as
seen
by
language
designers)
• Commodity
CPUs
are
mul$core
(e.g.
4
cores)
• Analogy:
you
have
4
assistants,
in
theory
you
could
get
the
work
done
4
$mes
faster!
• In
prac$ce:
– it’s
hard
because
you
now
have
to
figure
out
how
to
distribute
a
piece
of
work
amongst
4
people.
– It’s
easier
to
just
pass
the
whole
piece
of
work
to
one
person.
8
9. Why
Java
8?
(
as
seen
by
language
designers)
• Vast
majority
of
Java
programs
use
only
one
of
these
cores
and
leave
the
others
idle
• Why?
wri$ng
efficient
parallel
code
is
hard
– summing
an
array
with
a
for
loop
is
easy
– how
to
sum
an
array
on
4
cores?
9
10. Why
Java
8?
(
as
seen
by
language
designers)
• Java
8
introduces
features
that
make
parallel
data
processing
easier
• Driven
from
three
concepts:
– stream
processing
– behaviour
parameterisa$on
("passing
code")
– no
shared
mutable
data
10
15. No
shared
mutable
data
List<String>
lowCaloricDishesName
=
dishes.parallelStream()
.filter(d
-‐>
d.getCalories()
<
400)
.sorted(comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());
15
Can
be
duplicated
and
ran
on
disjoint
part
of
input
16. Java
8
• Introduces
a
concise
way
to
pass
behaviour
– lambda
expressions,
method
references
• Introduces
an
API
to
process
data
in
parallel
– Streams
API
– several
opera$ons
such
as
filter,
map,
reduce
can
be
parameterised
with
lambdas
• Also:
default
methods
(more
later)
– more
flexible
inheritance
16
17. Behaviour
parameterisa$on
• Goal:
abstract
over
behaviour
– Do
<something>
for
every
element
in
a
list
– Do
<something>
else
when
the
list
is
finished
–
Do
<yet
something
else>
if
an
error
occurs
• Why
should
you
care?
– Adapt
to
changing
requirements
– Java
8
Streams
API
heavily
relies
on
it
17
18. 1st
awempt:
fitering
green
apples
public
sta$c
List<Apple>
filterGreenApples(List<Apple>
inventory)
{
List<Apple>
result
=
new
ArrayList<>();
for(Apple
apple:
inventory){
if("green".equals(apple.getColor()
)
{
result.add(apple);
}
}
return
result;
}
18
19. 2nd
awempt:
abstrac$ng
color
public
sta$c
List<Apple>
filterApplesByColour(List<Apple>
inventory,
String
color)
{
List<Apple>
result
=
new
ArrayList<>();
for
(Apple
apple:
inventory){
if
(apple.getColor().equals(color)))
{
result.add(apple);
}
}
return
result;
}
19
List<Apple>
greenApples
=
filterApplesByColor(inventory,
"green");
List<Apple>
redApples
=
filterApplesByColor(inventory,
"red");
20. 2nd
awempt:
abstrac$ng
weight
public
sta$c
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;
}
20
List<Apple>
heavyApples
=
filterApplesByWeight(inventory,
150);
List<Apple>
megaHeavyApples
=
filterApplesByWeight(inventory,
250);
21. 3rd
awempt:
filtering
with
everything
public
sta$c
List<Apple>
filter
(List<Apple>
inventory,
String
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;
}
21
List<Apple>
greenApples
=
filter(inventory,
"green",
0,
true);
List<Apple>
heavyApples
=
filter(inventory,
"",
150,
false);
22. 4th
(a)
awempt:
modeling
selec$on
criteria
public
interface
ApplePredicate{
public
boolean
test
(Apple
apple);
}
public
class
AppleWeightPredicate
implements
ApplePredicate{
public
boolean
test(Apple
apple){
return
apple.getWeight()
>
150;
}
}
public
class
AppleColorPredicate
implements
ApplePredicate{
public
boolean
test(Apple
apple){
return
"green".equals(apple.getColor());
}
}
22
24. 4th
(b)
awempt:
filtering
by
an
abstract
criteria
public
sta$c
List<Apple>
filter(List<Apple>
inventory,
ApplePredicate
p){
List<Apple>
result
=
new
ArrayList<>();
for(Apple
apple:
inventory){
if(p.test(apple)){
result.add(apple);
}
}
return
result;
}
24
25. Let’s
pause
for
a
liwle
bit
• Our
code
is
much
more
flexible!
– can
create
any
kinds
of
selec$on
criteria
on
an
Apple
– re-‐use
of
code
for
filter
• However,
declaring
many
selec$on
criterias
using
classes
is
verbose…
L
– we
have
the
right
abstrac$on
but
not
good
concision
– we
need
a
bewer
way
to
create
and
pass
behaviours
25
26. 5th
awempt:
anonymous
classes
List<Apple>
result
=
filter(inventory,
new
ApplePredicate()
{
public
boolean
test(Apple
apple){
return
"red".equals.(apple.getColor());
}
});
List<Apple>
result
=
filter(inventory,
new
ApplePredicate()
{
public
boolean
test(Apple
apple){
return
apple.getWeight()
>
150;
}
});
26
27. 6th
awempt:
Java
8
lambdas
List<Apple>
result
=
filter(inventory,
(Apple
apple)
-‐>
"red".equals.(apple.getColor()));
List<Apple>
result
=
filter(inventory,
(Apple
apple)
-‐>
apple.getWeight()
>
150);
27
Great
because
closer
to
problem
statement!
28. 7th
awempt:
abstrac$ng
over
the
list
type
public
sta$c
<T>
List<T>
filter
(List<T>
list,
Predicate<T>
p)
{
List<T>
result
=
new
ArrayList<>();
for(T
e:
list){
if(p.test(e))
{
result.add(e);
}
}
return
result;
}
28
29. 8th
awempt:
Java
8
lambdas
again
List<String>
result
=
filter(strings,
(String
s)
-‐>
s.endsWith("JDK"));
List<Integer>
result
=
filter(numbers,
(Integer
i)
-‐>
i
%
2
==
0);
List<Apple>
result
=
filter(inventory,
(Apple
a)
-‐>
apple.getWeight()
>
150);
29
30. Moral
of
the
story
• Behaviour
parameterisa$on
lets
you
write
more
flexible
code
– Adapt
for
changes
– Avoids
code
duplica$on
• To
encourage
this
style
of
programming
we
need
a
concise
way
to
create
and
pass
behaviours
– Java
8
brings
lambda
expressions
to
help!
30
31. Flexible
Concise
Anonymous
classes
Verbose
Rigid
Lambdas
Class
+
Instance
Value
parameterisa$on
Behaviour
parameterisa$on
Value
vs
Behaviour
parameterisa$on
32. Lambda
expressions:
what
is
it?
• A
lambda
expression
is:
– a
kind
of
anonymous
func$on
– that
can
be
passed
around:
– It
doesn’t
have
a
name,
but
it
has
a
list
of
parameters,
a
body,
a
return
type,
and
also
possibly
a
list
of
excep$ons
that
can
be
thrown.
32
34. Lambda
expressions:
what
is
it?
• A
lambda
expression
is:
– a
kind
of
anonymous
func$on
• Anonymous:
doesn’t
have
an
explicit
name
like
a
method:
less
to
write
and
think
about!
• Func$on:
not
associated
to
a
class
like
a
method
is
34
35. Lambda
expressions:
what
is
it?
• A
lambda
expression
is:
– a
kind
of
anonymous
func$on
– that
can
be
passed
around:
• Passed
around:
A
lambda
expression
can
be
passed
as
argument
to
a
method
or
stored
in
a
variable
35
36. Lambda
expressions:
what
is
it?
• A
lambda
expression
is:
– a
kind
of
anonymous
func$on
– that
can
be
passed
around:
– it
doesn’t
have
a
name,
but
it
has
a
list
of
parameters,
a
body,
a
return
type,
and
also
possibly
a
list
of
excep$ons
that
can
be
thrown.
•
Concise:
you
don’t
need
to
write
a
lot
of
boilerplate
like
you
do
for
anonymous
classes.
36
37. Goal
• Goal:
let
you
pass
a
piece
of
behaviour/code
in
a
concise
way
• You
can
cope
with
changing
requirements
by
using
a
behaviour,
represented
by
a
lambda,
as
a
parameter
to
a
method
• You
no
longer
need
to
choose
between
abstracaon
and
concision!
37
38. Before/A`er
Before:
inventory.sort(new
Comparator<Apple>()
{
public
int
compare(Apple
a1,
Apple
a2){
return
a1.getWeight().compareTo(a2.getWeight());
}
});
A5er:
inventory.sort((Apple
a1,
Apple
a2)
-‐>
a1.getWeight().compareTo(a2.getWeight()));
38
39. Examples
39
Use
case
Example
of
lambda
A
boolean
expression
(List<String>
list)
-‐>
list.isEmpty()
Crea$ng
objects
()
-‐>
new
Apple(10)
Consuming
from
an
object
(Apple
a)
-‐>
System.out.println(a.getWeight())
Select/extract
from
an
object
(String
s)
-‐>
s.length()
Combine
two
values
(int
a,
int
b)
-‐>
a
*
b
Compare
two
objects
(Apple
a1,
Apple
a2)
-‐>
a1.getWeight().compareTo(a2.getWeight())
40. Where
and
how
to
use
lambdas?
40
• Java
lets
you
pass
expressions
only
where
a
type
is
expected
• So
what’s
the
type
of
a
lambda
expression?
41. Func$onal
interface
(1)
41
• The
type
of
a
lambda
expression
is
essen$ally
a
funcaonal
interface
• A
func$onal
interface
is
an
interface
that
declares
exactly
one
(abstract)
method.
42. Func$onal
interface
(2)
42
//
java.u$l.Comparator
public
interface
Comparator<T>
{
public
int
compare(T
o1,
T
o2);
}
public
interface
Runnable
{
public
void
run();
}
public
interface
ApplePredicate
{
public
boolean
test
(Apple
a);
}
43. Func$onal
interface
&
Lambdas
43
• The
signature
of
the
abstract
method
of
the
func$onal
interface
essen$ally
describes
the
signature
of
the
lambda
expression.
• Lambda
expressions
let
you
provide
the
implementa$on
of
the
abstract
method
of
a
func$onal
interface
directly
inline
and
treat
the
whole
expression
as
an
instance
of
a
func$onal
interface.
44. Func$onal
interface
&
Lambdas
44
Runnable
r1
=
()
-‐>
System.out.println("Hello
World
1");
Runnable
r2
=
new
Runnable(){
public
void
run(){
System.out.println("Hello
World
2");
}
}
public
void
process(Runnable
r){
r.run();
}
process(r1);
process(r2);
process(()
-‐>
System.out.println("Hello
World
3"));
46. Execute
Around
Pawern
46
public
sta$c
String
processFile()
throws
IOExcep$on
{
try
(BufferedReader
br
=
new
BufferedReader(new
FileReader("data.txt")))
{
return
br.readLine();
}
}
• This
current
code
is
limited.
You
can
read
only
the
first
line
of
the
file.
47. What
we
want
47
String
result
=
processFile((BufferedReader
br)
-‐>
br.readLine());
String
result
=
processFile((BufferedReader
br)
-‐>
br.readLine()
+
br.readLine());
48. Step
1:
Introducing
func$onal
interface
48
public
interface
BufferedReaderProcessor
{
public
String
process(BufferedReader
b)
throws
IOExcep$on;
}
public
sta$c
String
processFile(BufferedReaderProcessor
p)
throws
IOExcep$on
{
...
}
49. Step
2:
Execu$ng
a
behaviour
49
public
sta$c
String
processFile(BufferedReaderProcessor
p)
throws
IOExcep$on
{
try
(BufferedReader
br
=
new
BufferedReader(new
FileReader("data.txt")))
{
return
p.process(br);
}
}
String
result
=
processFile((BufferedReader
br)
-‐>
br.readLine());
String
result
=
processFile((BufferedReader
br)
-‐>
br.readLine()
+
br.readLine());
50. Lots
of
func$onal
interfaces
50
Funcaonal
interface
Lambda
signature
Predicate<T>
T
-‐>
boolean
Consumer<T>
T
-‐>
void
Func$on<T,
R>
T
-‐>
R
Supplier<T>
()
-‐>
T
UnaryOperator<T>
T
-‐>
T
Binaryoperator<T>
(T,
T)
-‐>
T
BiFunc$on<T,
U,
R>
(T,
U)
-‐>
R
• Have
a
look
in
java.u$l.func$on.*
51. Method
references
51
• Method
references
let
you
reuse
exis$ng
method
defini$ons
and
pass
them
just
like
lambdas.
«
First-‐class
»
func$ons
• Just
a
syntac'c
suggar
to
lambdas
Before:
(Apple
a)
-‐>
a.getWeight()
A5er:
Apple::getWeight
Before:
(str,
i)
-‐>
str.substring(i)
A5er:
String::substring
53. Bewer
code
with
Java
8:
a
prac$cal
example
53
public
class
AppleComparator
implements
Comparator<Apple>
{
public
int
compare(Apple
a1,
Apple
a2){
return
a1.getWeight().compareTo(a2.getWeight());
}
}
inventory.sort(new
AppleComparator());
54. Anonymous
class
54
inventory.sort(new
Comparator<Apple>()
{
public
int
compare(Apple
a1,
Apple
a2){
return
a1.getWeight().compareTo(a2.getWeight());
}
});
59. Tidy
up
59
import
sta$c
java.u$l.Comparator.comparing;
inventory.sort(comparing(Apple::getWeight));
Reads
like
probable
statement!
60. Streams
60
• Nearly
every
Java
applica$ons
makes
and
processes
collec$ons
• However
processing
collec$ons
is
far
from
perfect:
– SQL
like
opera$ons?
– How
to
efficiently
process
large
collec$ons?
61. SQL-‐like
opera$ons
61
• Many
processing
pawerns
are
SQL-‐like
– finding
a
transac$on
with
highest
value
– grouping
transac$ons
related
to
grocery
shopping
• Re-‐implemented
every
$me
• SQL
is
declaraave
– express
what
you
expect
not
how
to
implement
a
query
– SELECT
id,
MAX(value)
from
transac$ons
62. Example:
Java
7
62
List<Transac$on>
groceryTransac$ons
=
new
Arraylist<>();
for(Transac$on
t:
transac$ons){
if(t.getType()
==
Transac$on.GROCERY){
groceryTransac$ons.add(t);
}
}
Collec$ons.sort(groceryTransac$ons,
new
Comparator(){
public
int
compare(Transac$on
t1,
Transac$on
t2){
return
t2.getValue().compareTo(t1.getValue());
}
});
List<Integer>
transac$onIds
=
new
ArrayList<>();
for(Transac$on
t:
groceryTransac$ons){
transac$onsIds.add(t.getId());
}
sor$ng
by
decreasing
value
filtering
grocery
transac$ons
extract
Ids
65. Ok
cool
–
so
what’s
a
Stream?
65
• Informal:
a
fancy
iterator
with
database-‐like
opera$ons
• Formal:
A
sequence
of
elements
from
a
source
that
supports
aggregate
opera$ons.
66. Ok
cool
–
so
what’s
a
Stream?
66
• Sequence
of
elements:
a
stream
provides
an
interface
to
a
sequenced
set
of
values
of
a
specific
element
type.
However,
streams
don’t
actually
store
elements,
they
are
computed
on
demand.
67. Ok
cool
–
so
what’s
a
Stream?
67
• Source:
Streams
consume
from
a
data-‐providing
source
such
as
Collec$ons,
Arrays,
or
IO
resources.
68. Ok
cool
–
so
what’s
a
Stream?
68
• Aggregate
operaaons:
Streams
support
SQL-‐like
opera$ons
and
common
opera$ons
from
func$onal
programing
languages
such
as
filter,
map,
reduce,
find,
match,
sorted
etc.
69. Two
addi$onal
proper$es
69
• Pipelining:
Many
stream
opera$ons
return
a
stream
themselves.
This
allows
opera$ons
to
be
chained
and
form
a
larger
pipeline
as
well
as
certain
op$misa$ons
(more
later).
• Internal
iteraaon:
In
contrast
to
collec$ons,
that
are
iterated
explicitly
(“external
itera$on”),
stream
opera$ons
do
the
itera$on
behind
the
scene
for
you.
70. 70
Pipelining
• Intermediate
operaaons:
return
a
Stream
and
can
be
“connected”
• Terminal
operaaons:
computes
the
result
of
the
pipeline
Intermediate
opera$ons
Terminal
opera$on
78. Streams:
much
more(3)
Map<Boolean,
Map<Dish.Type,
List<Dish>>>
vegetarianDishesByType
=
menu.stream().collect(
paraaoningBy(Dish::isVegetarian,
groupingBy(Dish::getType)));
• Produces
a
two-‐level
Map:
{false={FISH=[prawns,
salmon],
MEAT=[pork,
beef,
chicken]},
true={OTHER=[french
fries,
rice,
season
fruit,
pizza]}}
78
79. Step
1:
sequen$al
for
loop
public
sta$c
long
itera$veSum(long
n)
{
long
result
=
0;
for
(long
i
=
0;
i
<
n;
i++)
{
result
+=
i;
}
return
result;
}
79
3ms
80. Step
2:
sequen$al
stream
public
sta$c
long
sequen$alSum(long
n)
{
return
Stream.iterate(1L,
i
-‐>
i
+
1)
.limit(n)
.reduce(Long::sum).get();
}
80
98ms
81. Step
3:
parallel
stream
public
sta$c
long
sequen$alSum(long
n)
{
return
Stream.iterate(1L,
i
-‐>
i
+
1).parallel()
.limit(n)
.reduce(Long::sum).get();
}
81
193ms!!
82. Step
4:
improved
sequen$al
stream
public
sta$c
long
rangedSum(long
n)
{
return
LongStream.rangeClosed(1,
n)
.reduce(Long::sum).getAsLong();
}
82
17ms
83. Step
5:
improved
parallel
stream
public
sta$c
long
rangedSum(long
n)
{
return
LongStream.rangeClosed(1,
n).parallel()
.reduce(Long::sum).getAsLong();
}
83
1ms
84. Streams:
much
more
(1)
84
• Infinite
streams
• Collectors:
– collect
– groupingBy
– par$$onBy
85. Op$onal<T>
85
• A
new
class
added
in
Java8:
java.u$l.Op$onal
• Indicates
the
presence
or
absence
of
a
value
• has
a
bunch
of
method
to
simplify
and
force
“null”
checking
Op$onal<Transac$on>
optTransac$on
=
numbers.stream().findAny(t
-‐>
t.getValue()
<
200);
87. Op$onal:
do
something
if
there’s
a
value
87
Instead
of:
if(transac$on
!=
null){
System.out.println(t);
}
Write:
optTransac$on.ifPresent(System.out::println);
93. Default
methods
93
• Interfaces
in
Java
8
can
contain
implementa$on
code
From
List.java:
default
void
sort(Comparator<?
super
E>
c){
Collec$ons.sort(this,
c);
}
94. Goal
94
• Evolve
libraries
• Adding
a
method
to
an
interface
is
source
incompa$ble
– all
implementers
need
to
support
the
method
– recompiling
a
class
implemen$ng
the
interface
will
fail
• Providing
a
“default”
implementa$on
solves
the
problem
97. Applica$ons:
mul$ple
inheritance
97
• A
class
has
been
allowed
to
implement
mul$ple
interfaces
since
day
1
of
Java!
public
class
ArrayList<E>
extends
AbstractList<E>
implements
List<E>,
RandomAccess,
Cloneable,
java.io.Serializable,
Iterable<E>,
Serializable
{
…
}
103. Resolu$on
rules
103
1.
Classes
always
win.
A
method
declara$on
in
the
class
or
a
superclass
takes
priority
over
any
default
method
declara$on.
2.
Otherwise,
the
method
with
the
same
signature
in
the
most
specific
default-‐providing
interface
is
selected.
(If
B
extends
A,
B
is
more
specific
than
A).
105. Use
case
1
105
public
interface
A{
public
default
void
hello()
{
System.out.println("Hello
from
A");
}
}
public
interface
B
extends
A{
public
default
void
hello()
{
System.out.println("Hello
from
B");
}
}
public
class
C
implements
B,
A
{
public
sta$c
void
main(String...
args)
{
new
C().hello();
//
??
}
}
106. Use
case
2
(a)
106
public
class
D
implements
A{
}
public
class
C
extends
D
implements
B,
A
{
public
sta$c
void
main(String...
args)
{
new
C().hello();
//
??
}
}
107. Use
case
2
(b)
107
public
class
D
implements
A{
public
void
hello(){
System.out.println(“Hello
from
D”);
}
}
public
class
C
extends
D
implements
B,
A
{
public
sta$c
void
main(String...
args)
{
new
C().hello();
//
??
}
}