Lambda expressions allow passing functions as arguments and simplify anonymous classes. Functional interfaces define a single abstract method that lambda expressions or method references can implement. Default methods enable adding new methods to interfaces without breaking existing code. Streams provide a declarative way to process collections through pipelines of intermediate and terminal operations. Other new features include date/time API improvements and the Optional class for null handling.
24. The java.util.function package contains many built in functional
interfaces:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Predicate<Person> canDrive = p -> p.getAge() > 16;
25. The java.util.function package contains many built in functional
interfaces:
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
Function<String, Integer> intToString = x ->
Integer.valueOf(x);
32. public interface Person {
void goSomewhere(Location location);
}
public Class NicePerson implements Person{
public void goSomewhere(Location location) {
…
}
}
33. Now Let’s add a method to the Person interface -
void saySomething (String whatToSay)
34. public interface Person {
void goSomewhere(Location location);
default void saySomething (String whatToSay) {
…
Sysout.println(whatToSay);
}
}
35. Adding a default method to an interface requires
no additions to the implementing classes.
36. Like any other method in the interface default
methods are implicitly public
37. Extending an interface with a default method:
Default method is not mentioned - extended
interface gets the default method.
38. Extending an interface with a default method:
Redeclaring the default method - method
becomes abstract (and implementing class has to
implement it).
39. Extending an interface with a default method:
Redefine the default method - override the
method functionality.
45. Default methods are never final
Default methods cannot be synchronized
Default methods are always public
There are no fields in an interface - hard to maintain a state
For most cases - classes define what an object is while an interface
defines what an object can do
Interfaces vs. Abstract Classes
47. A new Java API that helps processing data and
collections in a declarative way.
Formal definition: “A sequence of elements
supporting sequential and parallel aggregate
operations.”
48. The Pipeline Approach
Java streams use a pipeline approach to perform the different tasks
needed to process a given collection.
49. A pipeline is composed of the following:
Source - any structure that holds data: array,
collection, I/O channel, generator functions, etc…
50. A pipeline is composed of the following:
Intermediate operations - 0 or more operations
that produce a new stream. The basic examples
for this are the Stream() and Filter() operations.
51. A pipeline is composed of the following:
Terminal operation - an operation that returns a
non stream value e.g. primitives, collections or no
value at all.
53. double average = people
.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge)
.average()
.getAsDouble();
Produce a stream from the given collection
54. double average = people
.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge)
.average()
.getAsDouble();
Filter on a predicate - produces another
stream
55. double average = people
.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge)
.average()
.getAsDouble();
Map only the age of each person - return a
stream of type IntStream
56. double average = people
.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge)
.average()
.getAsDouble();
Perform average on all values - returns an
OptionalDouble
57. double average = people
.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge)
.average()
.getAsDouble();
Return the final value
58. int numberOfMales = 0;
int totalAge = 0;
foreach(person p : people){
if(p.getGender() == Person.Sex.MALE){
numberOfMales++;
totalAge += p.getAge();
}
}
if(numberOfMales > 0){
return totalAge / numberOfMales;
}
Old School Way:
59. Reduce
The average() operation from the previous example is one of
many reduction methods (the terminal operations)
The Java 8 API provides handy reduction operations but also a
more generic method to perform reduction by yourself
60. double agesSum = people
.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge)
.reduce(0, (a,b) -> a + b)
Identity - an initial and
also default value if the
stream is empty.
Accumulator - an
associative function
61. reduce also has a 3 parameter version that
accepts an identity, a mapper which replaces the
mapToInt in this case and a combiner function
that serves as an accumulator
62. Collect
The reduce method creates a new value for every element that it
processes.
The collect method modifies an existing value.
Think about creating the average ages in the last example (Yap it’s hard
with reduce…).
Let’s try it with collect:
63. Before we begin:
IntCosumer - a built in functional interface that accepts an int and
returns no result.
Defines a method called accept.
64. class Averager implements IntConsumer
{
private int total = 0;
private int count = 0;
public double average() {
return count > 0 ? ((double) total)/count : 0;
}
public void accept(int i) { total += i; count++; }
public void combine(Averager other) {
total += other.total;
count += other.count;
}
}
65. Averager averageCollect = people
.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.map(Person::getAge)
.collect(Averager::new, Averager::accept,
Averager::combine);
System.out.println("Average age of male members: " +
averageCollect.average());
Supplier - a factory function to
create new instances of the
result type
Accumulator - a function that
modifies the result container
Combiner - a function that
takes two result objects and
merges them into one
combined result
66. For most use cases we would probably want to
just run simple operations on a stream and get
some collection back as a result.
For this we can use Collectors:
69. Parallel Streams
Java streams also support parallel computation via parallelStream().
Just call it instead of stream() and the stream would be divided into
substreams and processed in a parallel manner.
This doesn’t guarantee faster execution times and really depends on
the hardware and application.
A big topic for another talk.
71. Optional
A new object wrapper that helps handling null pointer exceptions
Essentially wraps any object and exposes methods to make the object
handle NULL cases
78. But read this if it’s relevant: https://dzone.com/articles/introducing-new-date-and-time
79. More Stuff
- Additions to concurrency API’s - For another talk.
- IntelliJ suggestions are now Java 8 Ready - USE THEM!!!
- Samples in these slides are based on:
http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
- Great code samples for Java 8 - https://github.com/java8/Java8InAction
- Great article about streams -
http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-
2177646.html
81. Credits
Special thanks to all the people who made and released
these awesome resources for free:
Presentation template by SlidesCarnival
Photographs by Unsplash