{{announcement.body}}
{{announcement.title}}

How to use Optionals In Java

DZone 's Guide to

How to use Optionals In Java

Despite its controversy, Optional has greatly improved the design of Java applications. In this article, we look at how, when, and where to best apply Optionals.

· Java Zone ·
Free Resource

The introduction of the Optional class was a major improvement in the design of the Java language, but this improvement has been controversial. Prior the Optional class, many developers used either null or exceptions to denote when a desired value was not present; using the Optional class, however, allows us to explicitly state when a value may or may not be present. Despite this improvement, the Optional class can be applied inappropriately and cause more harm than good.

In this article, we will look at the fundamentals of the Optional class, including:

  • The purpose and the thought process behind its introduction
  • The essential methods included in the Optional class
  • The appropriate times and places to use the Optional class
  • A few alternative techniques that can be used instead

Purpose

Java, like most Object-Oriented (OO) languages, has a sneaky backdoor to its type system. For example, suppose that we have the following method signature:

Java
 




x


 
1
public Foo doSomething();


It is obvious that this method returns an object of type Foo, but it can also return another value: null. Since any non-primitive type can be set to null, there is nothing stopping a developer from writing the following method implementation:

Java
 




xxxxxxxxxx
1


1
public Foo doSomething() {
2
    return null; 
3
}


Nullity

This creates an onerous situation for the client that calls this method. Before using the Foo object returned from the doSomething method, a client must first check that the object is not null:

Java
 




x


1
Foo foo = doSomething();
2
 
          
3
if (foo == null) {
4
    // handle null case...
5
}
6
else {
7
    // do something with the foo object...
8
}


This approach ensures that the use of the Foo object does not result in a NullPointerException (NPE). This creates another problem, though: Any non-primitive object can be implicitly set to null. Thus, exhaustively, we must check every object returned from a method for nullity prior to using it. It goes without saying that this would not only be infeasible in a large application, but it would also muddle the clarity of our code. For example, adding extra lines for null-checks would introduce boilerplate code throughout our application that makes the use of the Foo object less clear (hidden within an if-else statement).

The root problem is that we do not know when a method intends to return null—such as when a desired value cannot be found—or can contractually guarantee that null will never be returned. Since we are unsure, we are forced to assume that any returned object can be null.

One solution that is commonly used is documenting that a return value can be null using JavaDocs. While this is an improvement over the original problem, this does not ensure that a client checks for nullity prior to the use of the object (i.e., the Java compiler will compile the code without these null-checks without complaint). Similarly, annotations like @NotNull exist, but these suffer from the same drawback as the documentation approach. Namely, enforcement can be circumvented.

Optional Class

Instead, we need a mechanism that allows a method developer to explicitly denote that the return value for a method may or may not be present. This mechanism was introduced in Java Development Kit (JDK) 7 in the form of the Optional class. This class acts as a wrapper for an object that may not be present. Therefore, if we know that our doSomething method may not return a desired Foo object, we can change the signature to:

Java
 




x


 
1
public Optional<Foo> doSomething();


As we will see in the following sections, Optional provides a suite of methods—many functional—that allows a client to decide what to do when the desired value is not present. For example, we can use the orElse method to return a default value when the desired value cannot be found (called an empty Optional in the Optional lexicon):

Java
 




xxxxxxxxxx
1


1
Foo foo = doSomething()
2
    .orElse(new Foo());


Likewise, we can also throw an exception when Optional is empty using the orElseThrow method:

Java
 




xxxxxxxxxx
1


 
1
Foo foo = doSomething()
2
    .orElseThrow(SomeException::new);


It is important to note two things:

  1. The Java compiler forces us to handle the case of an empty Optional value
  2. The client is responsible for handling a missing desired value

While documentation and annotations do move us in the correct—more-explicit—direction, they do not allow us to impose the responsibility of checking for a missing value on the client. Optional objects, on the other hand, require that a client make a decision about how to handle a missing value. 

For example, the following will not compile:

Java
 




x


1
Foo foo = doSomething();


We will instead see a compilation error alerting us that an object of type Optional<Foo> cannot be cast to Foo since the return type of doSomething is Optional<Foo> and the type of foo is Foo. Thus, we must call a method such as orElse or orElseThrow—or get, but we will see later why that should not be the first choice—in order to convert the Optional<Foo> object to a Foo object. Since both of those methods require a parameter, they, therefore, require us to make an explicit decision about what default value to use or what exception to throw if the Optional is empty.

Client Responsibility

This brings us to note (2): The responsibility for handling the empty Optional rests upon the client. In essence, the doSomething method—by nature of returning Optional<Foo> rather than Foo—is telling the client that there is a possibility that a result will not be found. Thus, the client is now responsible for handling the case when no result is found (i.e., one of the Optional methods, such as orElse, must be called to get from Optional<Foo> to Foo).

This client-responsible approach means that the method developer does not have enough information to determine what should be done in the case of a missing value. It is possible for the developer to throw an exception when the value cannot be found, but a missing value may not be a case that warrants an exception (i.e., it may not be an exceptional case). 

For instance, if we want to check if an object exists already, or create one if it does not, a non-existent object would not be an error and throwing an exception would not be warranted. What's more, throwing an exception would require that we catch the exception in the calling code in order to create the default. For example, suppose we create the following method:

Java
 




xxxxxxxxxx
1


 
1
public Foo findIfExists() throws FooNotFoundException;


To use the existing value, or create a default if one does not exist, we must do the following:

Java
 




xxxxxxxxxx
1


1
Foo foo = null;
2
 
          
3
try {
4
    foo = findIfExists();
5
}
6
catch (FooNotFoundException e) {
7
    foo = // create default value...
8
}


We can instead return an Optional value from findIfExists:

Java
 




xxxxxxxxxx
1


 
1
public Optional<Foo> findIfExists();


Then we can simply use the orElse method to provide a default value:

Java
 




xxxxxxxxxx
1


 
1
Foo foo = findIfExists()
2
    .orElse(/* create default value... */);


Additionally, the latter approach is much more readable. By simply reading the code, we understand that the two lines mean find if exists or else use this value. In the former case, we have to consciously derive the meaning of the catch clause to be the default value when findIfExists cannot find an existing value. Thus, the Optional lexicon matches the intended meaning much more closely than the exception approach.

Understanding this, Optional is a useful technique when a client is responsible for handling a missing object and the absence of the object is not an error. There may be times when a missing value is an error—such as when we assume a value exists and its absence could cause fatal results for the application—and a method should throw a checked or unchecked exception. In some cases, though (such as the findIfExists method), an absent object is not an error and using an Optional is an effective way to ensure that a missing object is explicitly handled by the client.

null Optional Objects

There is one caveat that must be addressed: It is possible for an Optional object to be null. Since Optional objects are non-primitive objects just like Foo, they can likewise be set to null. For example, the following implementation of doSomething will compile without error:

Java
 




xxxxxxxxxx
1


 
1
public Optional<Foo> doSomething() {
2
    return null;
3
}


This would lead to the bizarre case of the client having to handle both the case of a null return value and the case of handling an empty Optional:

Java
 




x


 
1
Optional<Foo> possibleFoo = doSomething();
2
 
          
3
if (possibleFoo == null) {
4
    // handle missing object case...
5
}
6
else {
7
    Foo foo = possibleFoo.orElse(/* handle missing object case... */); 
8
}


Not only does this introduce duplication for the missing object case (i.e., the case of a missing object is handled in two locations), but it also reintroduces the problem of reduced clarity. Instead, when an Optional is returned from a method, we should not check for a null value. According to the Optional class documentation:

A variable whose type is Optional should never itself be null; it should always point to an Optional instance.

If a null value is returned in place of an Optional object, this is a breach of the method contract on the part of the method developer. By stating that a method will return an Optional object, the method developer is also stating that it is invalid to return null. Since the Optional object denotes the possibility of a missing object, there is no valid use case for a null value (i.e., the method should return an empty Optional instead of null in all cases). 

Therefore, whenever we are dealing with an Optional object, we are right to assume that the Optional object will never be null. Although it is possible that the Optional object can be null in practice, this is a problem that should be resolved by the method developer rather than the client.

Important Methods

With an understanding of the concepts behind the Optional class, we can now look at how to use Optional objects in practice. The Optional class contains a large suite of methods, which can be broken down into two categories: Creation methods and instances method.

Creation Methods

The Optional creation methods are static methods that allow us to create various Optional objects to suit our needs. There are currently three such methods: One for creating a populated Optional (i.e., an Optional whose wrapped value is not null), one for creating a populated or empty Optional, and one for creating an empty Optional.

of

The static of method allows us to wrap an existing object with an Optional. If the existing object is not null, a populated Optional is returned:

Java
 




x


 
1
Optional<Foo> foo = Optional.of(new Foo());  // populated Optional


If the existing object is null, an NPE is thrown:

Java
 




xxxxxxxxxx
1


 
1
Optional<Foo> foo = Optional.of(null);      // throws NullPointerException


ofNullable

The ofNullable static method is identical to the of method when a non-null value is passed (i.e., a populated Optional is produced), but will produce an empty Optional when null is passed (i.e., an NPE will not be thrown):

Java
 




x


 
1
Optional<Foo> foo1 = Optional.ofNullable(new Foo());  // populated Optional
2
Optional<Foo> foo2 = Optional.ofNullable(null);       // empty Optional


This is method is commonly used when the nullity of an object is unknown.

empty

The empty static method simply creates an empty Optional:

Java
 




x


1
Optional<Foo> foo = Optional.empty();    // empty Optional


This method is, by definition, identical to:

Java
 




xxxxxxxxxx
1


 
1
Optional<Foo> foo = Optional.ofNullable(null);


As we will see in the following sections, the empty method is often used when it is known a priori that no value exists.

Instance Methods

The instance methods allow us to interact with existing Optional objects and focus primarily on querying the state of an Optional, obtaining the wrapped object from an Optional, and manipulating Optional objects.

isPresent & isEmpty

Two query methods are included in the Optional class that allows us to check whether a given Optional is populated or empty:

  1. isPresent: Returns true if the Optional is populated, or false otherwise
  2. isEmpty: Returns true if the Optional is empty, or false otherwise

Therefore, given a populated Optional, the query methods will return the following:

Java
 




x





1
Optional<Foo> populated = // ...populated Optional...
2
 
          
3
populated.isPresent();    // true
4
populated.isEmpty();      // false


Given an empty Optional, the query methods will return the following:

Java
 




x


 
1
Optional<Foo> empty = // ...empty Optional...
2
 
          
3
populated.isPresent();    // false
4
populated.isEmpty();      // true


get

The get method obtains the value wrapped by an Optional if the Optional is populated or throws a NoSuchElementException if the Optional is empty. This method is useful for converting an existing Optional to its value (i.e., from Optional<Foo> to Foo) when we can guarantee that the Optional is populated, but we should use this method sparingly.

In practice, guaranteeing that an Optional is populated requires that we first query the Optional using the isPresent or isEmpty methods and then call get:

Java
 




xxxxxxxxxx
1


 
1
Optional<Foo> possibleFoo = doSomething();
2
 
          
3
if (possibleFoo.isPresent()) {
4
    Foo foo = possibleFoo.get();
5
    // ...use the foo object...
6
}
7
else {
8
    // ...handle case of missing Foo...
9
}


The issue with this pattern is that is closely resembles the null-check that we performed before introducing the Optional. Thus, this approach removes the inherent benefits of the Optional class. In most cases, we should avoid using the get method and use one of the other methods—such as orElse or orElseThrow—to obtain the value associated with a populated Optional.

orElse Family

The orElse family of methods allows us to obtain the value wrapped by an Optional—if the Optional is populated—or a default method if the Optional is empty. The simplest method in this family is orElse, which takes an object of the wrapped type and returns it if the Optional is empty. For example, given an Optional<Foo> object, the orElse method takes a Foo object. If the Optional is populated, it returns the populated value; if the Optional is empty, it returns the Foo object that we passed to the orElse method:

Java
 




x





1
Optional<Foo> possibleFoo = doSomething();
2
 
          
3
Foo foo = possibleFoo
4
    .orElse(new Foo());


There are times, however, where the creation of a default value may be an expensive operation and may be unlikely to be used. For example, the default value may require that a connection be established to a remote server or may require an extended or large lookup from a database. If it is likely that Optional will be populated, it is unlikely that we will need the default value. With the orElse method, we are forced to create the default value even if it goes unused, which could lead to a serious performance impact.

The Optional class also includes the orElseGet method that takes a Supplier that can lazily create a default object. This allows the Optional class to create a default object only when needed (i.e., the default object is only created when if the Optional is empty). For example:

Java
 




x


1
Optional<Foo> possibleFoo = doSomething();
2
 
          
3
Foo foo = possibleFoo
4
    .orElseGet(() -> { /* ...lazily create a Foo object... */ });


orElseThrow Family

Similar to the orElse method, the Optional class provides an orElseThrow method that allows us to throw an exception when obtaining the wrapped value if the Optional is empty. Unlike the orElse methods, though, the orElseThrow method has two forms:

  1. A no argument form that throws an NoSuchElementException if the Optional is empty or returns the wrapped value if the Optional is populated
  2. A form that takes a Supplier that creates Throwable objects and throws the Throwable object if the Optional is empty or returns the wrapped value if the Optional is populated

For example, we can obtain a Foo object from an Optional<Foo> object as follows:

Java
 




x


 
1
Optional<Foo> possibleFoo = doSomething();
2
 
          
3
Foo foo = possibleFoo
4
    .orElseThrow();


If the Optional is empty, a NoSuchElementException will be thrown. If the Optional is populated, the wrapper value will be returned. The orElseThrow method, therefore, is identical to the get method in its functionality, but its name better describes what its purpose is. Thus, the orElseThrow method should be used anywhere it is value to throw a NoSuchElementException when an Optional is not populated, without first checking that it is populated (i.e., not using the isPresent or isEmpty query methods).

The get method should be reserved for use only when it is used within one of the Optional query methods (i.e., the populated or empty state of the Optional is checked first). Note that this orElseThrow method was introduced in JDK 9 as a way of reducing the confusion surrounding the use of the get method and should be favored over the get method.

According to Brian Goetz:

...One of the few mistakes we made [in Java 8] was the naming of Optional.get(), because the name just invites people to call it without calling isPresent(), undermining the whole point of using Optional in the first place....

During the Java 9 time frame, we proposed to deprecate Optional.get(), but the public response to that was ... let's say cold. As a smaller step, we introduced orElseThrow() in [Java] 10...as a more transparently named synonym for the current pernicious behavior of get().

The Optional class also includes an overloaded orElseThrow method that throws a custom exception when the Optional is empty. This method accepts a Suppler that creates any Throwable object—or object that is a subclass of Throwable—and throws it. For example:

Java
 




x


1
Optional<Foo> possibleFoo = doSomething();
2
 
          
3
Foo foo = possibleFoo
4
    .orElseThrow(() -> { /* ...lazily create a Foo object... */ });


This is useful when a client considers a missing object to be an error and wishes the throw an exception when accessing an empty Optional. It is also a common practice to throw simple exceptions using the functional form of the constructor:

Java
 




x


 
1
Optional<Foo> possibleFoo = doSomething();
2
 
          
3
Foo foo = possibleFoo
4
    .orElseThrow(SomeException::new);


ifPresent Family

The ifPresent method accepts a Consumer that performs an action using the wrapped value if the Optional is populated. This is a functional alternative to obtaining the wrapped object using the orElse or orElseThrow methods, primarily when we do not wish to perform any action when the value is not present. For example:

Java
 




x




1
Optional<Foo> possibleFoo = doSomething();
2
 
          
3
possibleFoo.ifPresent(foo -> { /* ...do something with foo... */ });


The Optional class also includes a similar method, ifPresentOrElse, that allows us to handle the case when the Optional is empty as well. The first argument accepted by the ifPresentOrElse method is a Consumer that performs an action using the wrapped value if the Optional is populated, while the second argument is a Runnable that performs an action if the Optional is empty. Thus, the Consumer is called only if the Optional is populated, while the Runnable is called only if the Optional is empty. For example:

Java
 




xxxxxxxxxx
1


 
1
Optional<Foo> possibleFoo = doSomething();
2
 
          
3
possibleFoo.ifPresentOrElse(
4
    foo -> { /* ...do something with foo... */ },
5
    () -> { /* ...do something when no foo found... */ }
6
);


The benefit of both of these methods is that the Consumer is never called if the Optional is empty. Likewise, in the case of ifPresentOrElse, the Runnable is never called if the Optional is populated. This allows us to provide complex or expensive operations that will be lazily called depending on the state of the Optional.

Note that this method should not just be used for expensive operations. This method should be used anytime an operation should be performed on a populated value. For example, if we wanted to update an object only if it exists, we could do something similar to the following:

Java
 




xxxxxxxxxx
1
10


1
public class Bar {
2
  
3
    private boolean isUpdated = false;
4
  
5
    public void update() {
6
        isUpdated = true;
7
    }
8
}
9
 
          
10
public Optional<Bar> findBar() {
11
    // ...return a populated Bar if it could be found...
12
}
13
 
          
14
findBar().ifPresent(bar -> bar.update());


In this case, we are not concerned with performing any action if the Bar object is not found. If we were, we could use the ifPresentOrElse method instead.

map

The map method allows us to convert a wrapped value from one object to another if an Optional is populated. This method can be thought of as a pipeline method, where the wrapped value is passed along the pipeline and transformed to a new value. This method works by accepting a Function object that is applied to the wrapped value to produce the mapped value. If the Optional is empty, the Function object is never called and an empty Optional is returned from the map method.

This method is useful when we do not know if a value is present, but if it is, it should be transformed to another object. This is a common use case when reading from a database, which usually stores Data Transfer Objects (DTOs). In most applications, the DTO is used to efficiently store a domain object in a database, but at the higher levels of the application, the domain object itself is needed. Therefore, we must convert from the DTO to the domain object. 

If we perform a lookup of a database object, we may or may not find the object. Thus, this is a good use case for returning an Optional wrapping the DTO. In order to convert to the domain object, we can use the map method. For example, suppose we have a DTO (PersonDto) that stores the name of a Person object in a single line while the Person object has the name split into first and last name (i.e., the name is stored as "John Doe" in the PersonDto object, but it is stored with a first name of "John" and a last name of "Joe" in the Person object). We can use a mapper object to convert from the PersonDto to Person object and use the mapper to map the PersonDto object returned from a database to a Person object:

Java
 






Note that it is possible to have a conversion that should result in an empty Optional. For example, if a conversion from a given object to another is not possible, then the map method should return an empty Optional. An anti-pattern for performing this technique is to have the Function object return null, which will then be wrapped by the map method—using ofNullable, which allows for our null object to be wrapped without throwing an exception—into an empty Optional:

Java
 






If the method dtoCanBeConverted evaluates to false, the Function object returns null which then results in person being an empty Optional. This approach is flawed because it reintroduces the insidious use of null values, whose replacement was the original purpose of the Optional class. Instead, we should use the flatMap method and explicitly return an empty Optional.

flatMap

The flatMap method is similar to the map method, except the flatMap accepts a Function object that converts the wrapped value to a new Optional. Unlike the map method, flatMap allows us to return an Optional of our choice. Thus, we can explicitly return an empty Optional if the mapping Function is unable to convert the wrapped value:

Java
 




x
10


 
1
Optional<Person> person = db.findPerson()
2
    .flatMap(dto -> {
3
        if (dtoCanBeConverted()) {
4
            Person person = return dao.fromDto(dto);
5
            return Optional.ofNullable(person);
6
        }
7
        else {
8
            return Optional.empty();
9
        }
10
    });


It is important to note that we are no longer able to simply return a Person object as we did with the map method. Instead, we are now responsible for wrapping the converted object into an Optional. Note that if the Function object returns a null Optional, an NPE is thrown. For example, the following will throw an NPE when executed:

Java
 




x
3


 
1
Optional<Person> person = db.findPerson()
2
    .flatMap(dto -> null);


filter

The filter method allows us to return a populated Optional if a populated Optional satisfies the supplied Predicate. Thus, if the filter method is applied to an empty Optional, the Predicate will not be called. Likewise, if the filter method is applied to a populated Optional, but the wrapped value does not satisfy the supplied Predicate (i.e., the test method of the Predicate object evaluates to false), an empty Optional is returned. For example:

Java
 




x


 
1
public class Bar {
2
  
3
    private int number;
4
  
5
    public Bar(int number) {
6
        this.number = number;
7
    }
8
  
9
    // ...getters & setters...
10
}
11
 
          
12
Predicate<Bar> greaterThanZero = bar -> bar.getNumber() > 0;
13
 
          
14
Optional.of(new Bar(1))
15
    .filter(greaterThanZero)
16
    .isPresent();              // true
17
 
          
18
Optional.of(new Bar(-1))
19
    .filter(greaterThanZero)
20
    .isPresent();              // false


When & When Not to Use

One of the most controversial aspects of the Optional class is when it should and should not be used. In this section, we will look at some of the common use cases, such as method return values, fields, and parameters, where Optionals may or may not be well suited.

Return Values

As we have seen throughout this article, Optionals are well suited for method return values because that was its intended purpose. According to the Optional class documentation:

Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors.

In general, an Optional should be used as a return value if:

  1. It is expected that a value may or may not be present
  2. It is not an error if a value is missing
  3. The client is responsible for handling the case of a missing value

Optional return values are often used for queries that may or may not find a desired object. For example, repositories will often be defined in the following manner:

Java
 




xxxxxxxxxx
1


 
1
public interface BookRepository {
2
    public Optional<Book> findById(long id);
3
    public Optional<Book> findByTitle(String title);
4
    // ...
5
}


This allows the client to handle a missing Book object—such as by ignoring a missing object, creating a default object, or throwing an exception—in a manner that is appropriate to the context in which the method was called.

Fields

While Optional objects are well-suited for return types, they are less apt for instance fields. It is possible to create a field similar to the following, but it is highly inadvisable:

Java
 




xxxxxxxxxx
1


 
1
public class Bar {
2
  
3
    private Optional<Foo> foo;
4
  
5
    // ...getters & setters...
6
}


Optional fields should be avoided because the Optional class is not serializable (i.e., does not implement the Serializable interface). According to Brian Goetz:

Of course, people will do what they want. But we did have a clear intention when adding this feature, and it was not to be a general purpose Maybe type, as much as many people would have liked us to do so. Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.

Therefore, Optional types are intended solely for method return types. Since fields constitute the internal state of a class and should not be visible to outside clients, if a field is considered optional, a getter can be created that returns an Optional object instead:

Java
 




x


 
1
public class Bar {
2
  
3
    private Foo foo;
4
  
5
    public Optional<Foo> getFoo() {
6
        return Optional.ofNullable(foo);
7
    }
8
}


Using this technique, clients are explicitly informed that the foo value may or may not be present, while the serializability of Bar is maintained.

Parameters

There are valid cases when a parameter to a method or constructor may be optional, but an Optional should not be used for this purpose. For example, the following should be avoided:

Java
 




xxxxxxxxxx
1


1
public class Bar {
2
  
3
    public void doSomething(Optional<Foo> foo) {
4
        // ...
5
    }
6
}


Instead of setting the parameter type to Optional, method overloading should be used:

Java
 




xxxxxxxxxx
1
10


1
public class Bar {
2
  
3
    public void doSomething() {
4
        // ...
5
    }
6
  
7
    public void doSomething(Bar bar) {
8
        // ...
9
    }
10
}


Additionally, non-overloaded methods—with different method names—can also be used:

Java
 




xxxxxxxxxx
1
10


 
1
public class Bar {
2
  
3
    public void doSomething() {
4
        // ...
5
    }
6
  
7
    public void doSomethingWithBar(Bar bar) {
8
        // ...
9
    }
10
}


Alternatives

While the Optional class is effective in the correct context, it is not the only approach that can be used when a desired value may or may not be found. In this section, we cover three alternatives to the Optional class and how they can be applied in the proper context.

null

The simplest alternative is to use null, as we saw at the beginning of this article. While this approach does accomplish our goal, after the introduction of the Optional class, nulls should only be used when Optional objects require too much overhead. This overhead can be in terms of the extra memory requires for the Optional wrapper class or in terms of the extra cycles requires to execute the Optional methods.

It may be tempting to use performance as an excuse to use nulls where Optionals would be more effective, but in a large majority of applications, the Optional class adds a trivial amount of overhead. Unless we are working with low-level code, just as bytes that come from a network or driver or we are working with extremely large amounts of data, Optionals should always be preferred over nulls for method return types.

Null Object

A more effective alternative than null values is to introduce a Null Object. A null object is an object that extends the desired type but contains the logic that would have been executed for the null case. For example, suppose we have the following code:

Java
 




x
10


1
public class Article {
2
  
3
    private long id;
4
  
5
    public void submit() {
6
        // ...
7
    }
8
  
9
    // ...getters & setters...
10
}
11
 
          
12
public class ArticleRepository {
13
  
14
    public Article findById(long id) {
15
        // ...return the article if it can be found...
16
    }
17
}
18
 
          
19
ArticleRepository repository = new ArticleRepository();
20
Article article = repository.findById(1);
21
 
          
22
if (article == null) {
23
    throw new ArticleNotFoundException();
24
}
25
else {
26
    article.submit();
27
}


We can refactor this code using a null object into the following:

Java
 




x


 
1
public class Article {
2
    // ...same as before...
3
}
4
 
          
5
public class NullArticle extends Article {
6
  
7
    @Override
8
    public void submit() {
9
        throw new ArticleNotFoundException();
10
    }
11
}
12
 
          
13
public class ArticleRepository {
14
   
15
    public Article findById(long id) {
16
      
17
        if (articleIsFound()) {
18
            // return article...
19
        }
20
        else {
21
            return new NullArticle();
22
        }
23
    }
24
}
25
 
          
26
ArticleRepository repository = new ArticleRepository();
27
Article article = repository.findById(1);
28
article.submit();


It is important to note that the introduction of a null object assumes that the method itself knows how to handle the case of a missing value. 

Exceptions

Another alternative that we saw in this article is to throw an exception when a desired object cannot be found. This approach works if the method knows that a failure to find a desired object is an error. For example:

Java
 




x


 
1
public class ArticleRepository {
2
   
3
    public Article findById(long id) {
4
      
5
        if (articleIsFound()) {
6
            // return article...
7
        }
8
        else {
9
            throw new ArticleNotFoundException();
10
        }
11
    }
12
}


Conclusion

There are many cases in which desired values may or may not be present in an application and handling these cases in a readable and effective manner is an important part of well-designed software. As of JDK 7, Java includes the Optional class, which allows developers to return values that may or may not be present and allows clients to handle these cases according to the context in which they occur. While the Optional class should only be used for method return values, understanding its usefulness and how to apply it using simple techniques is an important part of mastering modern Java.

Topics:
java, jdk, jdk 7, optional

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}