On a sunny day of March Oracle released Java 8 which introduced a lot of new features. Among these, the Optional (along with Streams and Lambdas) is the most useful in my opinion, and in the next few minutes I am going to show you why.

The billion-dollar mistake

If you have ever tried to write an application in Java which is a bit more complex than Hello World, you have probably already met with the infamous and fearsome NullPointerException. The first question which comes into our mind is usually: “Why is null good for us anyway?” Well, it is not.

The idea of the null reference was invented by Sir Tony Hoare.

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

  • Sir Tony Hoare (QCon London, 2009)

The actual damage is probably much higher than a billion-dollar by now, but who knew what it could cause in 1965?

Golden rules about null

I am pretty sure many of you have already read Robert C. Martin’s Clean Code (if not, I highly recommend it), which emphasizes two important things about using the null reference.

Don’t return null

Just simply don’t. If everybody were to follow the rule (or at least the developers who are working on the same project as you do), we wouldn’t have to bother with constant null checks, otherwise we have to be cautious all the time.

The question is: What can we do in a situation when the method should return a value, but we cannot instantiate the object, or we are not able to return a reference other than null? Well, if someone asks you this question in a job interview just simply look them in the eye and confidently say: “I would use Optional”. When they regain consciousness after this astonishing answer they would probably ask you how would you do that, but don’t worry, we will get there in a minute.

Don’t pass null

If you don’t return null, don’t pass null either. Sounds fair right? We don’t want the caller to do null checks all the time, and we certainly don’t want the method implementer to do the same. How can we avoid that? The answer is of course the mighty Optional.

What is Optional exactly?

Optional is a wrapper, which means it can contain a value or it can be empty. It has a rich API to define an alternative- value or behavior whenever we cannot get a valid reference.
Optional is immutable which means we cannot change the wrapped value after it has been initialized. (Keep in mind, there are no final objects in Java, only final references, therefore while the referred object can still be modified, the Optional cannot be)

Why is Optional so great?

If you return an Optional, you not only warn the caller, but you also provide a great toolkit to handle the absence of the value. In other words: “You should prepare for it, and here is how to deal with it”. This is an efficient and standard way to handle these situations, therefore we don’t have to worry about various solutions.
The other beauty of the Optional is that it can also be used as a method parameter, expressing the intent of the programmer, which means you can safely call it without a valid reference, and don’t have to worry about causing potential NullPointers.

 

How to use the Optional?

Let the examples speak for themselves:

Creating an Optional

  • Optional.empty() creates an Optional which does not have a value
Optional<String> opt1 = Optional.empty();
System.out.println(opt1);

Output:
Optional.empty

  • Optional.of throws a NPE if the given value is null
try {
	Optional<String> opt2 = Optional.of(null);
} catch (NullPointerException npe) {
	System.out.println(npe);
}

//Creating an Optional wrapping the "TestValue" String
Optional<String> opt3 = Optional.of("TestValue");
System.out.println(opt3);

Output:
java.lang.NullPointerException
Optional[TestValue]

  • Optional.ofNullable creates an empty Optional if the given value is null, otherwise it creates an Optional wrapping around a valid reference
//ofNullable does not throw an exception if the given value is null, it creates an empty Optional instead
Optional<String> opt4 = Optional.ofNullable(null);
System.out.println(opt4);

Output:
Optional.empty

 

Getting value from an Optional

In the following examples I will use the Optionals created above. Keep in mind:
-opt1 is empty
-opt3 contains the “TestValue” String

  • get() throws an Exception if the Optional is empty, otherwise it returns the wrapped value
//get() throws an Exception if the Optional is empty
//use it only if you previously checked it has a value in it
try{
	String s1 = opt1.get(); 
}catch(NoSuchElementException nsee){
	System.out.println(nsee);
}

String s2 = opt3.get();
System.out.println(s2);

Output:
java.util.NoSuchElementException: No value present
TestValue

  • orElse(T other) returns the wrapped value if it is present, if not, we can define an alternative value
String s3 = opt1.orElse("alternative value");
System.out.println(s3); //alternative value

String s4 = opt3.orElse("alternative value");
System.out.println(s4); //TestValue

Output:
alternative value
TestValue

  • orElseGet(Supplier s) is similar to orElse, but this method requires a Supplier, which is going to be called to produce a value if the Optional is empty
String s5 = opt1.orElseGet(() -> "alternative value");
System.out.println(s5); //alternative value

String s6 = opt3.orElseGet(() -> "alternative value");
System.out.println(s6); //TestValue

Output:
alternative value
TestValue

  • orElseThrow(Supplier s) returns the value if it is present (just like orElse and orElseGet), but this method will throw an Exception defined by a Supplier if the Optional is empty
try{
	String s7 = opt1.orElseThrow(() -> new RuntimeException("this exception will be thrown if value is not presented"));
}catch(RuntimeException re){
	System.out.println(re); //RuntimeException was thrown
}  

String s8 = opt3.orElseThrow(() -> new RuntimeException("this exception will be thrown if value is not presented"));
System.out.println(s8); //TestValue, no RuntimeException occurred

Output:
java.lang.RuntimeException: this exception will be thrown if value is not presented TestValue

 

How to check whether the Optional contains a value or not

  • isPresent() returns true if the Optional has a value, and false if the Optional is empty
System.out.println(opt1.isPresent()); //false, opt1 is empty
System.out.println(opt3.isPresent()); //true, opt3 wrapped the "TestValue" String

Output:
false
true

 

Execute a code block if the Optional is not empty

  • ifPresent(Consumer c) requires a Consumer as argument, and it will be executed if the Optional is not empty
//The Consumer's argument is the value wrapped by the Optional, not the Optional itself
opt1.ifPresent(value -> System.out.println("This won't be executed, because opt1 is empty"));
opt3.ifPresent(value -> System.out.println("This will be executed, wrapped value: " + value));

Output:
This will be executed, wrapped value: TestValue

 

Get an attribute from an object in “double safety”

Let me guide you through an example to show what I mean by that. Let’s create a Student class which has a String, and an Optional instance variable.

class Student{
    private String name;
    private Optional<Integer> scholarship;

    public Student(String name, Optional<Integer> scholarship) {
        this.name = name;
        this.scholarship = scholarship;
    }
//..getters, setters
}

For the demonstration I will use the following:

Optional<Integer> validScholarship = Optional.of(200);
Optional<Integer> emptyScholarship = Optional.empty();

Optional<Student> steveStudent = Optional.of(new Student("Steve", validScholarship));
Optional<Student> johnStudent = Optional.of(new Student("John", emptyScholarship));
Optional<Student> emptyStudent = Optional.empty();

As you can see, we have three Student Optionals. steveStudent has a valid scholarship value, John unfortunately hasn’t got any, and the third variable is not even a valid Student. Now let’s see our new tools in action:

 

flatMap(Function f)

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
	Objects.requireNonNull(mapper);
	if (!isPresent())
		return empty();
	else {
		return Objects.requireNonNull(mapper.apply(value));
	}
}

This is how the flatMap method looks like. If it’s a bit confusing, don’t worry, we will see how it works in a second.

First of all, flatMap can be called on an Optional, and it returns an Optional. This means that the object on which the flatMap is called can be empty, and the received value can be empty as well.

  • flatMap returns an empty Optional immediately, if the it does not contain a valid value
  • flatMap requires a Function, which is going to be used to specify which attribute should be returned and how
System.out.println(steveStudent.flatMap(student -> student.getScholarship()));
System.out.println(johnStudent.flatMap(student -> student.getScholarship()));
System.out.println(emptyStudent.flatMap(student -> student.getScholarship()));
//if the desired attribute is not an Optional already, we have to wrap it manually
System.out.println(emptyStudent.flatMap(student -> Optional.ofNullable(student.getName())));

Output:
Optional[200] (steveStudent has a valid scholarship)
Optional.empty (johnStudent hasn’t got any scholarship)
Optional.empty (emptyStudent is empty)
Optional[Steve] (steveStudent’s name was wrapped around by Optional manually)

Note that: instead of the lambda (student -> student.getScholarship()) we could use method reference: Student::getScholarship

  • if the desired attribute is not an Optional already, we have to wrap it manually
System.out.println(steveStudent.flatMap(student -> Optional.ofNullable(student.getName())));
System.out.println(emptyStudent.flatMap(student -> Optional.ofNullable(student.getName())));

Output:
Optional[Steve] Optional.empty

 

Sum of scholarships in Java 8

Now let’s assume we have a List of Student Optionals, and we want to calculate the sum of the scholarships. Keep in mind, the Student Optional can be empty, and if it is not, its scholarship still can be. Fortunately, using flatMap, we do not have to worry about null checks:

List<Optional<Student>> studentOptionalList = Arrays.asList(steveStudent, johnStudent, emptyStudent);

int scholarshipsSummary = 0;
for(Optional<Student> optStudent : studentOptionalList){
	scholarshipsSummary += optStudent.flatMap(student -> student.getScholarship()).orElse(0);
}
System.out.println(scholarshipsSummary);

Output:
200

Note that: We could use the Stream API as well. In this case the solution would look like this:

scholarshipsSummary = studentOptionalList
                .stream()
                .mapToInt(studentOpt -> studentOpt.flatMap(Student::getScholarship).orElse(0))
                .sum();
        System.out.println("scholarshipsSummary using stream: " + scholarshipsSummary);

Output:
scholarshipsSummary using stream: 200

The Stream API is rich, powerful, and convenient, but let’s set aside that discussion for another article.

If you are wondering how this code snippet would look like before Java 8:

Sum of scholarships before Java 8

class StudentV2 {
    private String name;
    private Integer scholarship;

    public StudentV2(String name, Integer scholarship) {
        this.name = name;
        this.scholarship = scholarship;
    }
//..getters, setters

Notice that scholarship is an Integer, not a primitive int, which means it can be null, and therefore it can throw NullPointer anytime when we want to use it as an int without null check.

StudentV2 steveStudentV2 = new StudentV2("Steve", 200);
StudentV2 johnStudentV2 = new StudentV2("John", null);
StudentV2 emptyStudentV2 = null;
List<StudentV2> studentV2List = Arrays.asList(steveStudentV2, johnStudentV2, emptyStudentV2);
int scholarshipsSummary2 = 0;
for(StudentV2 actualStudent : studentV2List){
	if(actualStudent != null && actualStudent.getScholarship() != null){
		scholarshipsSummary2 += actualStudent.getScholarship();
	}
}
System.out.println(scholarshipsSummary2);

Output:
200

 

map(Function f)

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
	Objects.requireNonNull(mapper);
	if (!isPresent())
		return empty();
	else {
		return Optional.ofNullable(mapper.apply(value));
	}
}

This is how the map method is implemented. It is very readable and self-explanatory, but let’s take a look at it more closely.

  • it returns an empty Optional immediately, if the Optional is empty
  • if the Optional is not empty, it will execute the function, and wrap the result by Optional.ofNullable automatically. This means, if the Function’s result is null, it will be treated safely, and the caller will get an empty Optional. Consequently, it can decide what the alternative value is going to be

For this example I will use the StudentV2 class again, which has two attributes: String name, and Integer scholarship.

Optional<StudentV2> steveStudentV2 = Optional.ofNullable(new StudentV2("Steve", 200));
Optional<StudentV2> johnStudentV2 = Optional.ofNullable(new StudentV2("John", null));
Optional<StudentV2> emptyStudentV2 = Optional.ofNullable(null);

Optional<Integer> stevesScholarship = steveStudentV2.map(StudentV2::getScholarship);
Optional<Integer> johnsScholarship = johnStudentV2.map(StudentV2::getScholarship);
Optional<Integer> emptysScholarship = emptyStudentV2.map(StudentV2::getScholarship);
System.out.println(stevesScholarship);
System.out.println(johnsScholarship);
System.out.println(emptysScholarship);

Output:
Optional[200]
Optional.empty
Optional.empty

 

Icing on the cake: Using the Optional in Spring

The Spring framework evolved quickly over the years, hence it is not surprising it adopted the Java 8 Optional. Without diving into the finer details of Spring, I will briefly consider how Spring Data benefits from it.

Assume we have the following repository:

@Repository
public interface PlayerRepository extends JpaRepository<Player, Long> {
    Player findByName(String name);
}

If you are not familiar with SpringData: the method’s implementation (which operates under the hood) will retrieve a Player entity by name. If no player can be found, it will return null. The coolest thing is that you can change the return type from Player to Optional, which means that from now on, this method will return an empty Optional instead of null.

@Repository
public interface PlayerRepository extends JpaRepository<Player, Long> {
    Optional<Player> findByName(String name);
}

If you have projects using SpringData written before Java 8 you might want to consider modifying your repositories.

Spring uses Optional in several places, for instance: Optional Dependency, RequestParam, RequestHeader and MatrixVariable.

Summary

I hope you found this useful, and remember the following key points:

  • Don’t return null
  • Don’t pass null
  • Use Optional to indicate the absence of a value
  • Optional is immutable

Tell this to your friends, colleagues, family members, and neighbours in order to live in a happy world where we don’t have to bother with null checks anymore.