Java Optional, NullPointerException Killer?

Reading Time: 7 minutes

Does the Java Optional Class finally rid us of NullPointerExceptions? I’ll demonstrate  how when Optional is used correctly and when combined with Java’s functional style of programming, that this is indeed possible.

Java Optional – We Don’t Say null Anymore

The Java Optional Class was introduced in order to reduce the number of nulls that can creep into your code base, of course null can lead to the infamous NullPointerException. The Optional class is essentially an immutable wrapper for a single Type <T> which may or may not be present.

In Optional land there are only two states …

Optional State Replaces
PRESENT if ( ref != null )
EMPTY if ( ref == null )

Empty is the new null with respect to Optional

We create Optional by simple static factory methods like

Conflict c = new Conflict();
Optional<Conflict> optConflict = Optional.of(c);
//If c was null then would get a NullPointerException
Optional<Conflict> optConflict = Optional.empty();
//No Value in here, its empty.

Conflict c = null;
Optional<Conflict> optConflict = Optional.ofNullable(c);
//If c is null then you get an empty Optional returned
//instead of an Exception like Optional.of

Trying to eliminate null values which cause NullPointerExceptions is a really big deal. Many of the errors I fix in the applications I maintain are actually caused by null. It’s actually the very first thing I look for when investigating a crash or misbehavior. Half my salary goes to null! It has been said, that the invention of null has lead to be a billion dollar industry mistake.

If you want to complement this tutorial with my YouTube Video Tutorial, check it out here (Part 1) …

When null is left unchecked and permitted to flow further down your code base, it becomes a ticking time bomb. When that bomb explodes at some future time, it’s very difficult to find out where the real source of null originated from.

Documenting that null may be returned from your method is a noble start but it doesn’t force the caller to have the same intentions.

Java Optional – Don’t Get Carried Away

I like to compare the Optional Class to checked exceptions! Let me explain.

Java forces us to handle a checked exception that could possibly be thrown from a method in order to enforce better error handling.  Remember when checked exceptions were in vogue? Heck, we had try/catch blocks all over the place. That was a great era – thanks JDBC! I’m sure you get my sarcasm but seriously, it can be a huge pain if used in the wrong places and everywhere.

Even if we are forced to catch a checked exception, it doesn’t mean we are forced to handle it properly. Below, we have a catch all and do nothing, hence leaving the door open for a failure down the line.

 try{
     connectToDB(url,user,pass);
 }catch(Exception e){
     //do nothing
 }

It’s the same thing with Optional. You don’t have to use it everywhere that you see null.

When your developing with your own Classes, you should handle null internally.

That’s right folks, null is here to stay so it’s business as usual for your Class internals. Yes (if != null) is still cool kids 😎

If you don’t handle null internally, you’ll find yourself using Optional everywhere which will complicate the internal processing, readability and add unnecessary overhead (Options do not come without a cost). Overall, we have the same issues as checked exceptions.

Java Optional – NullPointerException in Disguise

In the following example, we are forced to deal with a returned Optional however we assume that the value is present and attempt to extract its value. What if its empty?

Optional<Flight> optionalFlight
                        = this.findFlightById("WJ451");
Flight flight = optionalFlight.get(); //safe?

The problem is that we don’t first check if the value isPresent() before extracting the value. This is like playing  Russian roulette with Optional!

If the Flight Object/Value is actually present (which it is) then the above will work fine however if it was empty then we would get a NoSuchElementException. This is basically no better than getting a NullPointerException.

So what to do? A simple solution is to guard it.

Optional<Flight> optionalFlight = this.findFlightById("WJ451");
if ( optionalFlight.isPresent()){
    Flight flight = optionalFlight.get();
}

That’s safe but is quite similar to the classical if != null, right?  Still a step in a right direction though since your forced to deal with it. You could argue the above is a use case for a programmer who only has an imperative style to fallback on in favour over a functional style.

Let’s also not forget that there are things you can do in the body of the if statement that you simply cannot do with a lambda. Some cases that come to mind are access to local non-final variables,  returning based on some condition and throwing a checked exception. Therefore don’t be fooled into thinking it’s too basic to be of no use.

Java Optional – Bad Use Cases

There are cases where using Optional is just not advised. Some are not obvious and when first introduced to Optional, it’s easy to fall into the following traps.

Class fields/Instance variables

Possible code smell – does this optional field really belong in this Class? If so, the code will be more complicated to deal with on the calling side and internally. Maybe better to handle this internally as null.

If you absolutely decide on an Optional then beware that it isn’t  Serializable and if that’s a necessity, look into building a proxy to Serialize it for you.

public class Flight {
  int flightId;
  OptionalInt ssrCode; //Not Serializeable
  Optional<String> ssrMode; //Not Serializeable
}

Methods with optional input arguments

Best to implement method overloading instead . Keeps you on track for “single responsibility” principle.

//Don't do this with Optional. It's used below to 
//possibly determine if airport is known && domestic 
boolean isKnownAirport(Airport a, Optional<Boolean> domestic) {...}
//method overloading - better 
boolean isKnownAirport(Airport a){ ... }
boolean isKnownAirport(Airport a, boolean domestic){ ... }

Setters/Constructors

This is a pain point for the calling client to build the Optional that you need.

flight.setSSRCodeOnFlight (Optional.of(1234)); 
//versus ...
flight.setSSRCodeOnFlight (1234); // better

//Constructor
new Flight("SAT877", 
           OptionalInt.of(1234), 
           Optional.of("A"));
//versus
new Flight("SAT877",1234,"A"); //better

Has anyone had any issues returning an Optional from a getter? I’m wondering if 3rd party frameworks will trip on this since it goes against the JavaBean naming convention (names may not be in sync anymore). Let us know by commenting below.

Collections

There’s an overhead (16 bytes) to each Optional created which can eventually be felt by the GC especially when dealing with Collections. This is especially true if you have a Collection of Optional boxed types since this adds 2 levels of indirection for boxing/unboxing. This brings up an important point! Always use the specialisation classes OptionalInt, OptionalLong and OptionalDouble to avoid this type of double taxation.

Collections of Optional elements can be confusing to work with and maintain therefore strive to return an empty collection instead.

 List<Optional<Airport>> findAirport(Predicate<Airport> predicate)
 { ... }
 
 //better
 List<Airport> findAirport(Predicate<Airport> predicate)
 { ....
   return Collections.EMPTY_LIST;
 }

Java Optional – Recommendations

Follow these guidelines for using Optional effectively and you’ll reap the benefits of its intended purpose. If you remember nothing else, remember at least the following.

Perfect Use Case – Method Return Types

What and how you expose your method return types to the client code base is very important. If you know there is a possible scenario in which null may be returned then this is the perfect and recommended use case for Optional.

Not only is this self documenting but it will force the caller to take action, thus reducing the number of nulls floating around.

Here’s a case where an air traffic control system is executing an internal method which probes a flight for any potential conflicts. This is absolutely necessary before issuing a clearance (i.e Climb to 35,000 feet). Notice we are handling null internally but are returning Optional to the client.

 public Optional<Conflict> probeForConfict() {
     conflict = this.probeAirSpace ();
     return (conflict != null)
         ? Optional.of(conflict)
         : Optional.empty();
 }

The caller of the above method really needs to handle the possibility of a potential conflict with another aircraft. Returning null may cause a NullPointerException if not checked – not acceptable! The probeForConflict() method is making it pretty clear with Optional<Conflict> that you must handle that possibility now – or else people die!! Sorry, had to add that  😉

How To handle a Returned Optional Correctly

I showed earlier how we can use an imperative programming style with a safety guard with isPresent() and then executing a get().  Now lets tackle this from a functional programming style.

Replacing isPresent() and get() with map()

The example below uses the Optional  map() method to extract the conflict id via it’s mapping function but only if the value is present, This now replaces our previous need for isPresent() followed by a get() … nice! The map() method returns an Optional of that value – Optional<Integer> in this case.

If there is no value in the Optional then an empty Optional is returned by map() thus null isn’t propagated – kind of (I’ll explain in a sec).

Not letting null propagate further down

You should still handle the case were Optional is empty in order to avoid the caller running into null’s ugly cousin … NoSuchElementException.

There are several ways but lets take a look at the Optional method released in Java 9 method ifPresentOrElse. If the Optional is present then execute the following Consumer functional interface.

If map method returns an empty Optional then that will execute the Else clause which will execute a Runnable.

 Optional<Conflict> optionalConflict 
                                = flight.probeForConfict();

 optionalConflict
     .map(Conflict::getConflictId)
     .ifPresentOrElse(System.out::println, 
        () -> System.out.println("No Conflicts"));

Optional – Imperative Versus Functional

Replacing a simple if statement …

Replacing a simple if/else statement …

Java Optional Imperative Functional Table

Alternative to throwing an exception and filtering 

Java Optional Imperative Functional Table

Check out Part 2 of 2 of the YouTube Java Optional Tutorial

In Summary

With Optional a caller is now forced to make a decision on how to handle the case of an empty value being returned. We can’t say “ohh, I didn’t know that could return null” anymore. It’s also a good cover your a$$ policy if your the one writing the library method – like a conflict detection library.

I have to admit though that the caller can still decide  to do a bad job, just like catching a checked exception and doing nothing about it.

Is Optional the NullPointerException Killer? Well, if you treat all thrown checked exceptions as exception killers then the answer is yes 😉 ! In essence they are similar mechanisms, how you deal with that mechanism is what will make the difference. Optional attempts to keep you honest, now the question becomes …can you be?

The source code is available on GitHub – Java Optional

Leave a Reply

Your email address will not be published.