Explores and discusses benefits of functional programming in Java and how to program in a functional style. Watch Venkat Subramaniam's talk at https://youtu.be/Ee5t_EGjv0A if you would like to learn more.
2. 1
1 What is Functional Programming?
2 What are the Benefits?
3 Functions
4 Lambdas
5 Streams
6 Terminal Operators
7 Intermediate Operators
8 Collectors
9 Optional
Agenda
3. 2
What is Functional Programming?
Functions have no state
They don’t change
• the world around them
• their input
• access global state
Given the same input, they produce the same output
Just like a function in math: f(x) = y
What is Functional Programming
4. 3
What is Functional Programming?
Let’s face it – Computer application codebases have gotten HUGE!
How big are the codebases you work on?
Mutable state is frequently difficult to manage
Have you forgotten to set a flag too?
Imperative style code is often difficult to read
Don’t tell me what you’re doing – just do it!
Why Functional Programming?
5. 4
1 What is Functional Programming?
2 What are the Benefits?
3 Functions
4 Lambdas
5 Streams
6 Terminal Operators
7 Intermediate Operators
8 Collectors
9 Optional
6. 5
What are the Benefits?
Functional Programs are easier to:
• Scale
• Read
• Test
Faster!
7. 6
1 What is Functional Programming?
2 What are the Benefits?
3 Functions
4 Lambdas
5 Streams
6 Terminal Operators
7 Intermediate Operators
8 Collectors
9 Optional
8. 7
Functions
Functions are first-class citizens:
• They can be assigned, passed, and returned
just like variables!
• They don’t mutate state, they transform values
• Treating functions as values is known as First-
class Functions or Higher-order Programming
Functions as First-Class Citizens
12. 11
1 What is Functional Programming?
2 What are the Benefits?
3 Functions
4 Lambdas
5 Streams
6 Terminal Operators
7 Intermediate Operators
8 Collectors
9 Optional
13. 12
Lambdas
A lambda is an inline functional definition
They can see and use nearby values, but can’t
change them
Closures can change nearby variables.
Java 8 lambdas are NOT closures since they
require all variables to be final
What are Lambdas
14. 13
Lambdas
Before:
Comparator<Apple> byWeight = new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
};
After (with lambda expressions):
Comparator<Apple> byWeight =
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
Before and After Lambdas
15. 14
1 What is Functional Programming?
2 What are the Benefits?
3 Functions
4 Lambdas
5 Streams
6 Terminal Operators
7 Intermediate Operators
8 Collectors
9 Optional
16. 15
Streams
A sequence of elements supporting sequential and
parallel aggregate operations. (from Java 8 Javadoc)
Streams allow for you to manipulate collections of data
in a declarative way.
Operations can be chained together to form an
expressive processing pipeline
Streams perform internal iteration behind the scenes,
allowing you to focus on what rather than how
Streams can easily be parallelized by calling
parallelStream() -- YMMV
What is a Stream?
17. 16
Streams
Each intermediate operation performed on a stream returns another stream
This allows for any number of intermediate operations to be performed on a stream of elements
These operations include but are not limited to
filter()
map()
flatMap()
distinct()
Streams don’t perform an evaluation until they reach their terminal operation is reached, such as
reduce()
collect()
sum(), max(), min(), etc.
Streams Return Streams, and they’re Lazy!!!
18. 17
1 What is Functional Programming?
2 What are the Benefits?
3 Functions
4 Lambdas
5 Streams
6 Terminal Operators
7 Intermediate Operators
8 Collectors
9 Optional
19. 18
Terminal Operators
Terminal operators trigger processing and return a
value. reduce(), count(), anyMatch(), and
summaryStatistics() are examples of terminal
operations
Once a terminal operation takes place, that instance of
the stream is dead and no longer usable, and no further
processing can take place on it.
The End is Coming!
20. 19
Terminal Operators
Streams can only be traversed once, so how can we
reuse the code we wrote?
Assign the nonterminated stream operation to a stream
supplier object!
Supplier<Stream<String>> streamSupplier =
() -> Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
streamSupplier.get().anyMatch(s -> true); // ok
streamSupplier.get().noneMatch(s -> true); // ok
One Time Traversal and Stream Reuse
21. 20
1 What is Functional Programming?
2 What are the Benefits?
3 Functions
4 Lambdas
5 Streams
6 Terminal Operators
7 Intermediate Operators
8 Collectors
9 Optional
22. 21
Intermediate Operators
Intermediate Operators perform mapping / transforming and filtering operations on a stream of
elements.
Any number of intermediate operations can be performed on a stream of elements.
people.stream()
.filter(person -> person.getGender() == Gender.MALE)
.map(person -> new Person(person.getName().toUpperCase(), person.getGender(), person.getAge()))
.forEach(System.out::println);
Transforming! More Than Meets the Eye!
23. 22
1 What is Functional Programming?
2 What are the Benefits?
3 Functions
4 Lambdas
5 Streams
6 Terminal Operators
7 Intermediate Operators
8 Collectors
9 Optional
24. 23
Collectors
Collectors allow for objects to be accumulated to be
passed back to a result container such as a Collection or a
Map
Transformations may take place once all elements are
accumulated, but this is not required.
Collector<Employee, ?, Map<Department, Integer>>
summingSalariesByDept =
Collectors.groupingBy(Employee::getDepartment,
summingSalaries);
Finishing the Stream
25. 24
Collectors
Collectors require 3 things:
Empty instance of the output type
Operator that can put an element into that output type
Operation that can combine two of the output type
Collectors within collectors are possible too!
Requirements of Collectors
26. 25
1 What is Functional Programming?
2 What are the Benefits?
3 Functions
4 Lambdas
5 Streams
6 Terminal Operators
7 Intermediate Operators
8 Collectors
9 Optional
30. 29
Optional
public class Person {
private Optional<Car> car;
public Optional<Car> getCar() { return car; }
}
public class Car {
private Optional<Insurance> insurance;
public Optional<Insurance> getInsurance() { return insurance; }
}
public class Insurance {
private String name;
public String getName() { return name; }
}
Putting Optional to Work
31. 30
Optional
Empty optional creation
Optional<Car> optCar = Optional.empty();
Optional from a non-null value
Optional<Car> optCar = Optional.of(car);
Optional from a possibly null value
Optional<Car> optCar = Optional.ofNullable(car);
Creating Optionals
33. 32
Optional
This won’t compile!
Optional<Person> optPerson = Optional.of(person);
Optional<String> name =
optPerson.map(Person::getCar)
.map(Car::getInsurance)
.map(Insurance::getName);
Chaining Operations with Optionals
34. 33
Optional
Instead, use flatMap()
public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown");
}
Chaining Operations with Optionals
Notes de l'éditeur
Functions are isolated – they don’t know anything about and can‘t change the outside world
To allow secondary output to be passed out of a function, pass in a Consumer that will accept() this output, handing over control
Thank you to Venkat Subramaniam for this helpful comparison!
filter()
map()
flatMap() -- Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element.
It lets you replace each value of a stream with another stream and then concatenates all the generated streams into a single stream.
This will print out the name, gender, and age for each person object
If we wanted to create a collector to tabulate the sum of salaries by department, we could reuse the "sum of salaries" logic usingCollectors.groupingBy(Function, Collector):
Collectors within collectors are possible too! groupingBy() allows for a second downstream collector to be passed that will further reduce the elements of the stream and populate them as values in the resulting map instead of populating a list of values
A person might or might not have a car
A car might or might not be insured
There are several ways of creating optionals
Third example: If car were null, the resulting Optional object would be empty.
The map operation applies the provided function to each element of a stream. You could also think of an Optional object as a particular collection of data, containing at most a single element. If the Optional contains a value, then the function passed as argument to map transforms that value. If the Optional is empty, then nothing happens.
With streams, the flatMap method takes a function as an argument, which returns another stream.
This function is applied to each element of a stream, which would result in a stream of streams.
But flatMap has the effect of replacing each generated stream by the contents of that stream. In other words, all the separate streams that are generated by the function get amalgamated or flattened into a single stream.
What we want here is something similar, but you want to flatten a two-level optional into one.