3. The “Billion-Dollar mistake”
Tony Hoare the creator of the null reference
Created while designing ALGOL W
Meant to model the absence of a value
“… simply because it was so easy to implement”
The cause of the dreaded Null Pointer Exceptions
5. public class Person {
private Car car;
public Car getCar() { return car; }
}
public class Car {
private Insurance insurance;
public Insurance getInsurance() { return insurance; }
}
public class Insurance {
private String name;
public String getName() { return name; }
}
public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
6. public String getCarInsuranceName(Person person) {
if (person != null) {
Car car = person.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if(insurance != null) {
return insurance.getName();
}
}
}
return “Unknown”;
}
Attempt 1: Deep Doubts
7. public String getCarInsuranceName(Person person) {
if (person == null) {
return “Unknown”;
}
Car car = person.getCar();
if (car == null) {
return “Unknown”;
}
Insurance insurance = car.getInsurance();
if(insurance == null) {
return “Unknown”;
}
return insurance.getName();
}
Attempt 2: Multiple Exits
8. So what’s wrong with null?
It’s a source of error
NullPointerException
It bloats code
Worsens readability
It meaningless
Has no semantic meaning
It breaks Java’s philosophy
Java hides pointers from developers, except in one case
10. Optional
New class located at java.util.Optional<T>
Encapsulates optional values
It wraps the value if it’s there, and is empty if it isn’t
Optional.empty()
Signals that a missing value is acceptable
We can’t deference null, but we can dereference Optional.empty()
Car
Optional<Car> Optional<Car>
11. 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; }
}
Progressive Refactored
12. Warning before we progress!
Optionals are not intended to replace all null references
Design more comprehensible APIs
Forces caller to deal with the absence of a value
14. Creating Optional objects
Empty Optional
Optional<T> opt = Optional.empty();
Optional<Car> optCar = Optional.empty();
Optional from a non-null value
Optional<T> opt = Optional.of(T value);
Optional<Car> optCar = Optional.of(car);
Optional from a null value
Optional<T> opt = Optional.ofNullable(T value);
Optional<Car> optCar = Optional.ofNullable(car);
15. How do we use Optional?
Optional<Car> optCar = Optional.ofNullable(car);
Car realCar = optCar.get(); DO NOT DO THIS! (ANTI-PATTERN)
Throws a NoSuchElementException if empty
Same conundrum as using null
17. Filtering (Extracts)
// Get the unique even number from a list
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0) // get even number
.distinct()
.forEach(System.out::println); // 2, 4
Stream<T> filter(Predicate<? super T> predicate)
Returns a stream consisting of the elements of this stream that match the given
predicate.
18. Mapping (Transforms)
// Get the length of the each word in a list
List<String> words = Arrays.asList("Hello", "World");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
System.out.println(wordLengths); // [5, 5]
<R> Stream<R> map(Function<? super T,? extends R> mapper)
Returns a stream consisting of the results of applying the given function to the
elements of this stream.
19. Back to our scheduled programming
(no pun intended)
The best ways to use Optional
20. Optional<Insurance> optInsurance = Optional.ofNullable(insurance)
Optional<String> name = optInsurance.map(Insurance::getName)
Extracting and Transforming Values
String name = null;
if(insurance != null){
name = insurance.getName()
}
public class Insurance {
private String name;
public String getName() { return name; }
}
Get the name of an insurance policy that may not be there.
21. Chaining Optionals
Optional<Person> optPerson = Optional.ofNullable(person)
Optional<String> name = optPerson.map(Person::getCar)
.map(Car::getInsurance)
.map(Insurance::getName);
public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
Get the name of an insurance policy that a person may have.
• Doesn’t compile
• getCar returns Optional<Car>
• Results in Optional<Optional<Car>>
22. FlatMapping
Optional<Person> optPerson = Optional.ofNullable(person)
Optional<String> name = optPerson.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName);
public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
Get the name of an insurance policy that a person may have.
• flatMap flattens multi-level operations into a single operation
• flatMap performs on the value inside of the Optionals
23. Bit of Refactoring
Public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
// default value if Optional is empty
.orElse(“Unknown”);
}
public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
Get the name of an insurance policy that a person may have.
24. Filtering with Optionals
Insurance insurance = new Insurance(“Progressive”);
Optional<Insurance> optInsurance = Optional.of(insurance);
optionalInsurance.filter(i -> “Progressive”.equals(insurance.getName()))
// Print the name if it exsists.
.ifPresent(x -> System.out.println(“ok”));
Insurance insurance = new Insurance(“Progressive”);
If (insurance != null && “Progressive”.equals(insurance.getName())){
System.out.println(“ok”);
}
Check to see if an insurance name is equal to “Cambridge”
25. Combining Maps and Filters
Public String getCarInsuranceName(Optional<Person> person, int minAge) {
return person.filter(age -> p.getAge() >= minAge)
.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
// default value if Optional is empty
.orElse(“Unknown”);
}
Get the name of an insurance policy that a person may have, only if the person’s
age is greater than a certain number.
27. Wrapping Potentially Null Values
Map<String, Object> map = new HashMap<>();
// Potentially returns a null value
Object value = map.get(“key”);
// Safely transforms a potential null into an Optional
Optional<Object> value = Optional.ofNullable(map.get(“key”));
• Existing APIs mostly return null for absent values
• We’d prefer if they returned an Optional
• Can’t change them, but we wrap them
28. Throwing Exceptions
Public String getCarInsuranceName(Optional<Person> person, int minAge) {
return person.filter(age -> p.getAge() >= minAge)
.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
// Throw an appropriate exception
.orElseThrow(IllegalStateException::new);
}
Sometimes you need to throw an exception if a value is absent.
29. Optional Methods
Method: Description:
empty Returns an empty Optional instance
filter Returns the Optional value if it matches the predicate;
otherwise returns an empty Optional
flatMap If a value is present, it returns the Optional resulting
from the application of the provided mapping function;
otherwise returns an empty Optional
get Returns the wrapped value if present; throws an
exception if not
ifPresent If the value is present, it invokes the Consumer;
otherwise does nothing
isPresent Returns true if there’s a value
30. Optional Methods
Method: Description:
map If a value is present, applies the mapping function
of Returns an Optional that wraps the value if it’s not null
ofNullable Returns an Optional that wraps the value if it’s not null;
Returns an empty Optional if it is null
orElse Returns the value if present; otherwise does the default
given
orElseGet Returns the value if present; otherwise invokes the
Supplier
orElseThrow Returns the value if present; otherwise throws the
exception
31. Summary
Null references have been historically introduced to signal absences
Optional has been introduced to model presence or absence
Can create Optional objects using factory methods:
Optional.empty
Optional.of
Optional.ofNullable
Optional supports methods suited for functional programming
Using Optional forces callers to actively deal with potential absences
Optional can help you design better APIs