Java 8 Best Practices Stephen Colebourne
Java 8 Best Practices Stephen Colebourne
A personal viewpoint
Stephen Colebourne, October 2015
Agenda
● ⇒ Introduction
● λ Lambdas
● ⒳ Functional interfaces
● Exceptions
● Optional
● ♒ Streams
● I Interfaces
● Date and Time
● Extras
Introduction
⇒
Introduction
● What is a Best Practice?
Introduction
● What is a Best Practice?
λ
Lambdas
● Block of code
○ like an anonymous inner class
● Always assigned to a Functional Interface
○ an interface with one abstract method
● Uses target typing
○ context determines type of the lambda
Lambdas - Example
// Java 7
List<Person> people = loadPeople();
Collections.sort(people, new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name);
}
});
Lambdas - Example
// Java 7
List<Person> people = loadPeople();
Collections.sort(people, new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p1.name.compareTo(p2.name);
}
});
Lambdas - Example
// Java 8
List<Person> people = loadPeople();
Collections.sort(people,
);
Lambdas - Example
// Java 8
List<Person> people = loadPeople();
Collections.sort(people,
(Person p1, Person p2) -> p1.name.compareTo(p2.name));
Lambdas - Example
// Java 8
List<Person> people = loadPeople();
Collections.sort(people,
(Person p1, Person p2) -> p1.name.compareTo(p2.name));
Lambdas - Example
// Java 8
List<Person> people = loadPeople();
Collections.sort(people,
(p1, p2) -> p1.name.compareTo(p2.name));
Lambdas - Example
// Java 8
List<Person> people = loadPeople();
people.sort((p1, p2) -> p1.name.compareTo(p2.name));
Lambdas Best
Practice
// prefer
(p1, p2) -> p1.name.compareTo(p2.name);
// avoid
(Person p1, Person p2) -> p1.name.compareTo(p2.name);
Lambdas Best
Practice
// prefer
str -> str.toUpperCase(Locale.US);
// avoid
(str) -> str.toUpperCase(Locale.US);
Lambdas Best
Practice
// prefer
str -> str.toUpperCase(Locale.US);
// new method
public static DoubleMatrix2D of(
int rows, int columns, IntIntToDoubleFunction valueFunction)
Functional interfaces
⒳
Functional interfaces
● An interface with a single abstract method
○ Runnable
○ Comparable
○ Callable
● Java SE 8 adds many new functional interfaces
○ Function<T, R>
○ Predicate<T>
○ Supplier<T>
○ Consumer<T>
○ see java.util.function package
Functional interfaces Best
Practice
@FunctionalInterface
public interface FooBarQuery {
public abstract Foo findAllFoos(Bar bar);
}
Higher order methods
● Methods accepting lambdas are nothing special
○ declared type is just a normal interface
● However there are some subtleties
// avoid
public class Foo<T> {
public Foo<R> apply(Function<T, R> fn);
public Foo<T> apply(UnaryOperator<T> fn);
}
Avoid method overloads Best
Practice
// prefer
public class Foo<T> {
public Foo<R> applyFunction(Function<T, R> fn);
public Foo<T> applyOperator(UnaryOperator<T> fn);
}
Functional interface last Best
Practice
// prefer
public Foo parse(Locale locale, Function<Locale,Foo> fn);
// avoid
public Foo parse(Function<Locale,Foo> fn, Locale locale);
Exceptions
!
Checked exceptions
● Most functional interfaces do not declare exceptions
● No simple way to put checked exceptions in lambdas
Unchecked.wrap(() -> {
// any code that might throw a checked exception
});
Testing for exceptions
● Complete unit tests often need to test for exceptions
https://www.flickr.com/photos/bigmacsc99/4003751542/
Optional and null
● New class 'Optional' added to Java 8
● Polarizes opinions
○ functional programming dudes think it is the saviour of the universe
● Simple concept - two states
○ present, with a value - Optional.of(foo)
○ empty - Optional.empty()
Optional and null
● Standard code using null
// application code
Foo foo = findFoo(key);
if (foo == null) {
foo = Foo.DEFAULT; // or throw an exception
}
Optional and null
● Standard code using Optional
// application code
Foo foo = findFoo(key).orElse(Foo.DEFAULT);
// or
Foo foo = findFoo(key).orElseThrow(RuntimeException::new);
Optional and null Best
Practice
// prefer
Foo foo = findFoo(key).orElse(Foo.DEFAULT);
// avoid
Optional<Foo> optFoo = findFoo(key);
if (optFoo.isPresent()) { … }
Optional Best
Practice
A. Use everywhere
B. Use instead of null on public APIs, input and output
C. Use instead of null on public return types
D. Use in a few selected places
E. Do not use
Optional Best
Practice
A. Use everywhere
B. Use instead of null on public APIs, input and output
C. Use instead of null on public return types
↖ my preferred choice ↗
D. Use in a few selected places
E. Do not use
Optional
● Optional is a class
● Some memory/performance cost to using it
● Not serializable
● Not ideal to be an instance variable
● JDK authors added it for return types
● Use in parameters often annoying for callers
● Use as return type gets best value from concept
http://blog.joda.org/2015/08/java-se-8-optional-pragmatic-approach.html
Streams
♒
Streams
● Most loops are the same
● Repetitive design patterns
● Stream library provides an abstraction
● Lambdas used to pass the interesting bits
Streams
List<Trade> trades = loadTrades();
List<Money> valued = new ArrayList<Money>();
for (Trade t : trades) { Loop to build output
list from input
if (t.isActive()) {
Only interested in
Money pv = presentValue(t); some trades
valued.add(pv);
Converts each trade
} to the money value
}
Streams
List<Trade> trades = loadTrades();
List<Money> valued = new ArrayList<Money>();
for (Trade t : trades) {
if (t.isActive()) {
Money pv = presentValue(t);
valued.add(pv);
}
}
Streams
List<Trade> trades = loadTrades();
List<Money> valued = new ArrayList<Money>();
for (Trade t : trades) {
if (t.isActive()) {
Money pv = presentValue(t);
valued.add(pv);
}
}
Streams
List<Trade> trades = loadTrades();
List<Money> valued = List
trades
t.isActive()
presentValue(t)
Streams
List<Trade> trades = loadTrades();
List<Money> valued = // List
// trades
// t.isActive()
// presentValue(t)
Streams
List<Trade> trades = loadTrades();
List<Money> valued = // List
trades.stream() // trades
// t.isActive()
// presentValue(t)
Streams
List<Trade> trades = loadTrades();
List<Money> valued = // List
trades.stream() // trades
.filter(t -> t.isActive()) // t.isActive()
// presentValue(t)
Streams
List<Trade> trades = loadTrades();
List<Money> valued = // List
trades.stream() // trades
.filter(t -> t.isActive()) // t.isActive()
.map(t -> presentValue(t)) // presentValue(t)
Streams
List<Trade> trades = loadTrades();
List<Money> valued = // List
trades.stream() // trades
.filter(t -> t.isActive()) // t.isActive()
.map(t -> presentValue(t)) // presentValue(t)
.collect(Collectors.toList());
Streams
List<Trade> trades = loadTrades();
List<Money> valued =
trades.stream()
.filter(t -> t.isActive())
.map(t -> presentValue(t))
.collect(Collectors.toList());
Streams
● Streams are great, sometimes
● Important not to get carried away
● Design focus was on Collections, not Maps
● Key goal was simple parallelism
Streams
List<Trade> trades = loadTrades();
List<Money> valued =
trades.stream()
.filter(t -> t.isActive())
.map(t -> presentValue(t))
.collect(Collectors.toList());
Streams
List<Trade> trades = loadTrades();
List<Money> valued =
trades.parallelStream()
.filter(t -> t.isActive())
.map(t -> presentValue(t))
.collect(Collectors.toList());
Streams Best
Practice
● Do not overdo it
● Stream not always more readable than loop
● Good for Collections, less so for Maps
● Don't obsess about method references
○ IntelliJ hint may not be the best idea
Streams Best
Practice
List<Money> valued =
trades.stream()
.filter(t.isActive())
.map((Trade t) -> presentValue(t))
.collect(Collectors.toList());
Streams
● Learn to love 'Collector' interface
● Complex, but useful
● Sometime necessary to write them
● Need collectors for Guava 'ImmutableList' and friends
○ see 'Guavate' class in OpenGamma Strata
Interfaces
I
Interfaces
● Now have super-powers
● Default methods
○ normal method, but on an interface
● Static methods
○ normal static method, but on an interface
● Extend interfaces without breaking compatibility
● Cannot default equals/hashCode/toString
Interfaces Top
Tip
● New macro-design options
● Instead of factory class, use static method on interface
● Instead of abstract class, use interface with defaults
● Result tends to be fewer classes and better API
Interfaces Best
Practice
LocalDate ✔ ❌ ❌ ❌ 2015-12-03
LocalTime ❌ ✔ ❌ ❌ 11:30
LocalDateTime ✔ ✔ ❌ ❌ 2015-12-03T11:30
OffsetDateTime ✔ ✔ ✔ ❌ 2015-12-03T11:30+01:00
ZonedDateTime ✔ ✔ ✔ ✔ 2015-12-03T11:30+01:00
[Europe/London]
// prefer
LocalDate date = LocalDate.of(2015, 10, 15);
// avoid
Temporal date = LocalDate.of(2015, 10, 15);
Rocket powered
Other features
● Base64
● Arithmetic without numeric overflow
● Unsigned arithmetic
● StampedLock
● CompletableFuture
● LongAdder/LongAccumulator
● Enhanced control of OS processes
Other Features
● Enhanced annotations
● Reflection on method parameters
● No PermGen in Hotspot JVM
● Nashorn JavaScript
● JavaFX is finally ready to replace Swing
Try a Java 8 open source library
● JOOL
○ https://github.com/jOOQ/jOOL
● ThrowingLambdas
○ https://github.com/fge/throwing-lambdas
● Parts of OpenGamma Strata (strata-collect - Guavate)
○ https://github.com/OpenGamma/Strata
● But beware excessively functional ones
○ most push ideas that don't really work well in Java
Immutability
● Favour immutable classes
● Lambdas and streams prefer this
● Preparation for value types (Java 10?)
● Use Joda-Beans to generate immutable "beans"
○ http://www.joda.org/joda-beans/
Summary
Summary
● Java 8 is great
● Can be quite different to Java 7 and earlier
● Vital to rethink coding style and standards
○ methods on interfaces make a big difference
● Beware the functional programming/Scala dudes
○ a lot of their advice is simply not appropriate for Java