Look ma!
I am a functional programmer
Java 8 in Action
Michał Urbanek
What is it about?

Why should I care?
- only java version with support
- java 7 is EOL since April 2014
- AppEngine will (once) migrate

It's cool!
Back in the day

Main motivations?


Functional Programming
- immutability
- pure functions
- first class and higher order functions
- pattern matching
- tail recursion
- declarative
- hipster :)


What's new?
- Default methods
- Lambdas
- Streams
- Parallel Data Processing
- Optionals
- New Date and Time API
- Completable Future
- ...

“Java is still not dead -
and people are starting to figure that out.”
Default methods

Default methods
- virtual extension methods
- non-abstract method implementations in interfaces
- help APIs to evolve in compatible way
- static methods in interfaces
Optional methods
public interface Sized {
int size();
default boolean isEmpty() {
return size() == 0;
}
}
interface Iterator<T> {
boolean hasNext();
T next();
default void remove() {
throw new UnsupportedOperationException();
}
}
Multiple inheritance
of behaviour
Resolution Rules
- Classes always win
- Otherwise, sub-interfaces win
- Finally, if choice is ambiguous then class has to explicitly select method implementation
Abstract class vs. Interface
Class can extend only from one abstract class, but a class can implement multiple interfaces.
Abstract class can enforce a common state trough instance variables (fields). An interface can't have instance variables.
Lamda Expressions

Behaviour parametrization
oldschool
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("Hello world");
}
});
Lambda expression
- Anonymous - doesn't have a name
- Function - it has list of params, body and return type
- Passed Around - can be argument
- Concise - no boiler plate code
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("Hello world");
}
});
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, (String a, String b) -> b.compareTo(a));
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, (a, b) -> b.compareTo(a));
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
names.sort((a, b) -> b.compareTo(a));
Thread t = new Thread(() -> { System.out.println("Hello world"); });
Thread t = new Thread(() -> System.out.println("Hello world"));
Behaviour parametrization
newschool
(String s) -> s.length
(Apple a) -> a.getWeight() > 150
() -> 42
(int x, int y) -> {
System.out.println("Result:");
System.out.println(x + y);
}
(parameters) -> expression
(parameters) -> { statements; }
Syntax and examples
if (logger.isDebugEnabled()) {
loger.debug("Cause: " + generateDiagnostics());
}
Deferred execution
loger.debug(() -> "Cause: " + generateDiagnostics());
Functional interfaces
- must contain exactly one abstract method declaration
- as many default methods as possible
- can be annotated with @FuncitonalInterface
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
Common functional interfaces
Predicate<T> | T -> boolean |
Consumer<T> | T -> void |
Function<T, R> | T -> R |
Supplier<T> | () -> T |
UnaryOperator<T> | T -> T |
BinaryOperator<T> | (T, T) -> T |
BiPredicate<L, R> | (L, R) -> boolean |
Method references
- reuse existing method definitions
- pass them just like lambdas
- improve readability
- use :: keyword (nothing to do with C++)
- can be also used for constructors

Streams

Streams
- implement java.util.Stream
- sequence of elements on which you can perform operations
- manipulate collections in a declarative way
- internal vs external iteration
- intermediate vs. terminal operations
Streams vs. Collections
Collection | Stream |
---|---|
Egarly Constructed | Computed on demand |
Values spread out in space | Values spread out in time |
Traversable many times | Traversable only once |
External iteration | Internal iteration |
Intermediate vs. Terminal
Intermediate | Stream |
---|---|
Return Stream | Produce result |
Lazy | Eager |
Can be chained | Final |
e.g. filter, map, limit, sorted, distinct | e.g. forEach, count, collect, sum, min |
Why bother?

Parallelism just got easy
Simply change stream() to parallelStream()
animals.stream().filter(Animal::isPredator).count()
animals.parallelStream().filter(Animal::isPredator).count()
and let your extra cores do the job!!
Always measure!
Optional
- source of error
- bloats the code
- meaningless
- breaks java philosophy
- creates a hole in the type system
Problems with null?
Optional
- model the absence of a value
- it's strongly typed
- forget about the NullPointerException
- less bloated code
Optional
Optional.empty();
Optional.of("Just a text");
Optional.ofNullable("This can be null");
Creation
Optional<String> optional = Optional.of("String");
optional.isPresent();
optional.ifPresent(s -> System.out.println(s));
optional.map(String::length).get();
optional.filter(s -> s.length() > 3).orElse("Default");
optional.orElseGet(() -> "Default");
optional.orElseThrow(() -> new IllegalArgumentException());
Operations
Optional
private String getInsuranceName(Person person) {
if (person != null) {
Car car = person.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if (insurance != null) {
String name = insurance.getName();
return name == null ? UNKNOWN_NAME : name;
}
}
}
return UNKNOWN_NAME;
}
Null checks made easy
private String getInsuranceNameOptional(Person person) {
return Optional.ofNullable(person)
.flatMap(Person::getCarOptional)
.flatMap(Car::getInsuranceOptional)
.map(Insurance::getName)
.orElse(UNKNOWN_NAME);
}
Map improvements
Map<Integer, String> map = new HashMap<>();
// put value only if not already in map
map.putIfAbsent(5, "val5");
// iterate over entries
map.forEach((key, val) -> System.out.println(val));
// compute new value from old value
map.computeIfPresent(3, (key, val) -> key + num);
// compute new value only if not already in map
map.computeIfAbsent(23, key -> "val" + key);
// remove key only if given value
map.remove(3, "val3");
Array operations
int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// sort array in parallel
Arrays.parallelSort(array);
// fill array with computed values in parallel
Arrays.parallelSetAll(array, i -> array.length - i);
// compute array's prefixes in parallel
Arrays.parallelPrefix(array, Integer::sum);
New Date API
- representing date and time for both humans and machines
- dealing with different timezones and calendars
- separation of chronologies
- package java.time.*
- immutable value classes
- thread-safe
- heavily inspired by joda-time
New Date API
// access to the current date and time (timezone aware)
Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();
// access to timezones
ZoneId.getAvailableZoneIds();
ZoneId.of("Europe/Berlin");
// time representation
LocalTime now = LocalTime.now(zone1);
LocalTime late = LocalTime.of(23, 59, 59);
now.isBefore(late);
long hoursBetween = ChronoUnit.HOURS.between(now, late);
// date representation
LocalDate today = LocalDate.now();
LocalDate yesterday = today.minusDays(1);
// date and time representation
LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);
DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
DateTimeFormatter.ofPattern("MMM dd, yyyy - HH:mm").format(sylvester);
CompletableFuture
- create asynchronous computation and retrieve its result
- merging asynchronous operations
- reacting to the completion of an asynchronous operation
CompletableFuture
List<String> result = IntStream.range(0, 4)
.mapToObj(i -> CompletableFuture.supplyAsync(
() -> Computation.compute(i), executor))
.map(future -> future.thenApply(String::valueOf))
.map(future -> future.thenCompose(
s -> CompletableFuture.supplyAsync(() -> "value" + s, executor)))
.map(CompletableFuture::join)
.collect(Collectors.toList());
What's next?


What's next?
Java 9
- Project Jigsaw aka Modules
- Light-weight JSON API
- HTTP 2 Client
- Process API Improvements
- Improved contended locking
References
- Raoul-Gabriel Urma et al. "Java 8 in Action"
- Richard Warburton "Java 8 Lambdas"
- https://github.com/winterbe/java8-tutorial
- https://docs.oracle.com/javase/tutorial/java
/javaOO/lambdaexpressions.html

Look ma! I am a functional programmer! - Java 8 in Action
By urbmic
Look ma! I am a functional programmer! - Java 8 in Action
- 1,034