28

Take the two code examples:

if(optional.isPresent()) {
    //do your thing
}

if(variable != null) {
    //do your thing
}

As far as I can tell the most obvious difference is that the Optional requires creating an additional object.

However, many people have started rapidly adopting Optionals. What is the advantage of using optionals versus a null check?

4
  • 4
    you almost never want to use isPresent, you should usually be using ifPresent or map
    – jk.
    Feb 3, 2016 at 11:20
  • 6
    @jk. Because if statements are sooooo last decade, and everyone's using monad abstractions and lambdas now.
    – user253751
    Feb 3, 2016 at 22:21
  • 2
    @immibis I once had a lengthy debate over this topic with another user. The point is that in if(x.isPresent) fails_on_null(x.get) you exit the type system and have to keep the guarantee that the code won't break "in your head" over the (admittedly short) distance between the condition and the function call. In optional.ifPresent(fails_on_null) the type system makes this guarantee for you, and you don't have to worry.
    – ziggystar
    Feb 5, 2016 at 8:34
  • 1
    The major flaw in Java with using Optional.ifPresent (and various other Java constructs) is that you can only modify effectively final variables and cannot throw checked exceptions. That is reason enough to often avoid ifPresent, unfortunately.
    – Mike
    Apr 7, 2017 at 17:10

9 Answers 9

22

Optional harnesses the type system for doing work that you'd otherwise have to do all in your head: remembering whether or not a given reference may be null. This is good. It's always smart to let the compiler handle boring drugework, and reserve human thought for creative, interesting work.

Without Optional, every reference in your code is like an unexploded bomb. Accessing it may do something useful, or else it may terminate your program wth an exception.

With Optional and without null, every access to a normal reference succeeds, and every reference to an Optional succeeds unless it's unset and you failed to check for that. That is a huge win in maintainability.

Unfortunately, most languages that now offer Optional haven't abolished null, so you can only profit from the concept by instituting a strict policy of "absolutely no null, ever". Therefore, Optional in e.g. Java is not as compelling as it should ideally be.

4
  • 1
    Since your answer is the top one, it might be worth adding the usage of Lambdas as an advantage - for anyone reading this in the future (see my answer)
    – Will
    Feb 5, 2016 at 8:51
  • Most modern languages provide non nullable types, Kotlin, Typescript, AFAIK.
    – Zen
    Nov 9, 2017 at 9:23
  • 7
    IMO this is better handled with a @Nullable annotation. No reason to use Optional just to remind you that the value could be null
    – Hilikus
    Apr 19, 2018 at 21:15
  • 3
    @Hilikus But which nullable annotation should I use? As far as I can tell the JDK doesn't have one. There's one in javax.annotations that seems to be provided by FindBugs, and another in org.jetbrains.annotations that is provided by IntelliJ. Using the former gives me a warning because of inconsistent use (?). It smells like a can of worms to me.
    – flodin
    May 19, 2020 at 6:39
23

An Optional brings stronger typing into operations that may fail, as the other answers have covered, but that is far from the most interesting or valuable thing Optionals bring to the table. Much more useful is the ability to delay or avoid checking for failure, and to easily compose many operations that may fail.

Consider if you had your optional variable from your example code, then you had to perform two additional steps that each might potentially fail. If any step along the way fails, you want to return a default value instead. Using Optionals correctly, you end up with something like this:

return optional.flatMap(x -> x.anotherOptionalStep())
               .flatMap(x -> x.yetAnotherOptionalStep())
               .orElse(defaultValue);

With null I would have had to check three times for null before proceeding, which adds a lot of complexity and maintenance headaches to the code. Optionals have that check built in to the flatMap and orElse functions.

Note I didn't call isPresent once, which you should think of as a code smell when using Optionals. That doesn't necessarily mean you should never use isPresent, just that you should heavily scrutinize any code that does, to see if there is a better way. Otherwise, you're right, you're only getting a marginal type safety benefit over using null.

Also note that I'm not as worried about encapsulating this all into one function, in order to protect other parts of my code from null pointers from intermediate results. If it makes more sense to have my .orElse(defaultValue) in another function for example, I have much fewer qualms about putting it there, and it's much easier to compose the operations between different functions as needed.

10

It highlights the possibility of null as a valid response, which people often assume (rightly or wrongly) is not going to be returned. Highlighting the few times when null is valid allows omitting littering your code with huge numbers of pointless null checks.

Ideally setting a variable to null would be a compile time error anywhere but in an optional; eliminating runtime null pointer exceptions. Obviously backwards compatibility precludes this, sadly

4

Let me give you an example:

class UserRepository {
     User findById(long id);
}

It's quite clear what is this method intended for. But what happens if the repository doesn't contain user with given id? It could throw an exception or maybe it returns null?

Second example:

class UserRepository {
     Optional<User> findById(long id);
}

Now, what happens here if there's no user with given id? Right, you get Optional.absent() instance and you can check it with Optional.isPresent(). Method signature with Optional is more explicit about its contract. It's immediatelly clear, how the method is supposed to behave.

It also has a benefit when reading the client code:

User user = userRepository.findById(userId).get();

I have trained my eye to see .get() as an immediate code smell. It means that client purposefully ignores the contract of the invoked method. It's much easier to see this than without Optional, because in that case you're not immediatelly aware what's the contract of called method (whether it allows null or not in its return) so you need to check it first.

Optional is used with the convention that methods with return type Optional never return null and usually also with the (less strong) convention that method without Optional return type never return null (but it's better to annotate it with @Nonnull, @NotNull or similar).

4

In addition to the other answers, the other major advantage of Optionals when it comes to writing clean code is that you can use a Lambda expression in the event that the value is present, as shown below:

opTr.ifPresent(tr -> transactions.put(tr.getTxid(), tr));
3

An Optional<T> allows you to have "failure" or "no result" as a valid response/return value for your methods (think, e.g., of a database lookup). Using an Optional instead of using null to indicate failure/no result has some advantages:

  • It clearly communicates that "failure" is an option. The user of your method does not have to guess whether nullmight be returned.
  • The value null should, at least in my opinion, not be used to indicate anything as the semantics might not be totally clear. It should be used to tell the garbage collector that the previously referred to object may be collected.
  • The Optional class provides many nice methods to conditionally work with the values (e.g., ifPresent()) or define default values in a transparent way (orElse()).

Unfortunately, it does not completely remove the need for null checks in the code as the Optional object itself still might be null.

4
  • 5
    This is not really what Optional is for. To report an error, use an exception. If you can't do that, use a type which contains either a result or an error code. Feb 7, 2016 at 0:42
  • I strongly disagree. Optional.empty() is a great way to show failure or non-existence in my opinion. Oct 12, 2017 at 12:29
  • 1
    @LegendLength, I must strongly disagree in the case of failure. If a function fails, it'd better give me an explanation, not just an empty, "I failed" Oct 12, 2017 at 17:51
  • Sorry I agree, i mean 'expected failure' as in not finding an employee with name 'x' during a search. Oct 13, 2017 at 1:55
2

Consider the following method:

void dance(Person partner)

Now let's look at some calling code:

dance(null);

This causes a potentially difficult to track crash somewhere else, because dance was not expecting a null.

dance(optionalPerson)

This causes a compile error, because dance was expecting a Person not an Optional<Person>

dance(optionalPerson.get())

If I didn't have a person, this causes an exception on this line, at the point where I illegitimately tried to convert the Optional<Person> into a Person. The key is that unlike passing a null, the except traces to here, not some other location in the program.

To put it all together: misusing optional produces easier to track down problems, misusing null causes difficult to track down problems.

4
  • 1
    If you're throwing exceptions when calling Optional.get() your code is badly written - you should almost never be calling Optional.get without checking Optional.isPresent() first (as indicated in the OP) or you could write optionalPersion.ifPresent(myObj::dance). Feb 4, 2016 at 9:19
  • @QmunkE, yes, you should only call Optional.get() if you already know that optional isn't empty (either because you called isPresent or because your algorithm guarantees it). However, I do worry about people blindly checking isPresent and then ignoring the absent values creating a host of silent failures which will be a pain to trace. Feb 4, 2016 at 20:33
  • 2
    I like optionals. But I will say that plain old null pointers are very rarely a problem in real-world code. They nearly always fail near the offending line of code. And I think that gets overblown by people when talking about this subject. Oct 12, 2017 at 12:25
  • @LegendLength, my experience is that 95% of cases are indeed readily traceable to identify where the NULL comes from. The remaining 5%, however, are extremely difficult to track down. Oct 12, 2017 at 17:33
-1

In Objective-C, all object references are optional. Because of this, code like you posted is silly.

if (variable != nil) {
    [variable doThing];
}

Code like the above is unheard of in Objective-C. Instead, the programmer would just:

[variable doThing];

If variable contains a value, then doThing will be called on it. If not, no harm. The program will treat it as a no-op and continue running.

This is the real benefit of optionals. You don't have to litter your code with if (obj != nil).

1
  • Isn't that just a form of silently failing though? In some cases you might care that the value is null, and want to do something about it. Oct 12, 2017 at 12:27
-2

if(optional.isPresent()) {

The obvious problem here is that if "optional" really is missing (i.e. is null) then your code is going to blow up with a NullReferenceException (or similar) trying to call any method on a null object reference!

You might want to write a static, Helper-class method that determines the null-ness of any given object type, something like this:

function isNull( object o ) 
{
   return ( null == o ); 
}

if ( isNull( optional ) ) ... 

or, perhaps, more usefully:

function isNotNull( object o ) 
{
   return ( null != o ); 
}

if ( isNotNull( optional ) ) ... 

But are either of these really any more readable/ understandable/ maintainable than the original?

if ( null == optional ) ... 
if ( null != optional ) ... 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.