Dustin Marx is a software developer who enjoys identifying and using the correct tool for the job. In addition to writing software and writing a blog on software development, Dustin occasionally presents at conferences and writes articles. Dustin is a DZone MVB and is not an employee of DZone and has posted 234 posts at DZone. You can read more from them at their website. View Full User Profile

Guava's New Optional Class

12.30.2011
| 9564 views |
  • submit to reddit

Guava Release 10 introduces the Optional class, which can be used where one might use a null object. I have built my own classes like this before, but the advantage of Guava's providing it is that it can be easily reused across projects and developers new to an organization might know about it already if they use Guava. In addition, use of the Guava class is advantageous when compared to use of one's own similar or null object class because of the normal advantages associated with open source communities such as more testing and more eyes on the code.

As I wrote about in the post Effective Java NullPointerException Handling, I generally don't like to have methods return null. With methods that return collections, returning an empty collection is an easy solution. When methods return arbitrary objects, however, it is not always easy to indicate an "empty" condition. Guava's Optional class can fill this need nicely.

The Optional class has no constructors, but provides three public static methods for acquiring an instance of the class. Optional.fromNullable(T) allows a null or non-null reference to be provided and wrapped in the new Optional instance. If the passed-in parameter is null, then the instance does not have any reference stored and is an "absent" instance. If the passed-in parameter is not null, then that non-null reference is stored within the new Optional instance.

Another publicly available static method for getting an instance of Optional is Optional.of(T). This method acts like Optional.fromNullable(T) except that it expects a non-null parameter to be passed to it. If null is passed to it, a NullPointerException is thrown.

The third static method for acquiring an instance of Optional is Optional.absent(). This is useful when one has code that knows the parameter that would have been provided to Optional.fromNullable(T) is null and it is more clear to express than an "absent" version of Optional should be returned.

Once an instance of Optional has been acquired (such as returned by a method), there are several instance methods that can be called on that instance. The Optional.isPresent() method is useful for determining if a given Optional instance has a non-null parameter within it.

Once it is known (such as by calling Optional.isPresent()) that an Optional instance contains a non-null reference, the Optional.get() method returns that stored non-null reference. Note that if there is no non-null reference, an exception is thrown upon this method's invocation, making it a good idea to call isPresent() first.

There are several overloaded or methods that allow defaults to be specified that are returned when the Optional instance does not contain its own non-null reference. These methods provide a nice way to specify a default value to be returned when it would be null returned otherwise (or an exception thrown). There may be times, especially when interacting with pre-existing APIs, that it is desirable to return a null rather than an "absent" Optional instance. This is easily accomplished through use of Optional.orNull() which returns either the reference type the instance contains or else returns null if it doesn't contain a non-null reference.

Many of the concepts discussed above are exemplified in the following code listing.

GuavaOptional.java
package dustin.examples;

import static java.lang.System.out;

import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Demonstrate use of Guava's Optional class.
 * 
 * @author Dustin
 */
public class GuavaOptional
{
   /** java.util.logging Logger handle. */
   private final static Logger LOGGER = Logger.getLogger(GuavaOptional.class.getCanonicalName());

   /** Map of state names to the names of that state's capital. */
   private final static Map<String, String> stateCapitals;

   static
   {
      final Map<String, String> tempStatesToCapitals = Maps.newHashMap();
      tempStatesToCapitals.put("Alaska", "Juneau");
      tempStatesToCapitals.put("Arkansas", "Little Rock");
      tempStatesToCapitals.put("Colorado", "Denver");
      tempStatesToCapitals.put("Idaho", "Boise");
      tempStatesToCapitals.put("Utah", "Salt Lake City");
      tempStatesToCapitals.put("Wyoming", "Cheyenne");
      stateCapitals = Collections.unmodifiableMap(tempStatesToCapitals);
   }

   /**
    * Provide the name of the capital of the provided state. This method uses
    * Guava's Optional.fromNullable(T) to ensure that a non-null Optional instance
    * is always returned with a non-null contained reference or without a
    * contained reference.
    * 
    * @param stateName State whose capital is desired.
    * @return Instance of Optional possibly containing the capital corresponding
    *    to provided the state name, if available.
    */
   public Optional<String> getStateCapital(final String stateName)
   {
      return Optional.fromNullable(stateCapitals.get(stateName));
   }

   /**
    * Provide quotient resulting from dividing dividend by divisor.
    * 
    * @param dividend Dividend used in division.
    * @param divisor Divisor used in division.
    * @return Optional wrapper potentially containing Quotient from dividing
    *    dividend by divisor.
    */
   public Optional<BigDecimal> getQuotient(final BigDecimal dividend, final BigDecimal divisor)
   {
      BigDecimal quotient;
      try
      {
         quotient = dividend.divide(divisor);
      }
      catch (Exception ex)
      {
         LOGGER.log(Level.SEVERE, "Unable to divide " + dividend + " by " + divisor + "-", ex);
         quotient = null;
      }
      return Optional.fromNullable(quotient);
   }

   /**
    * Main function for demonstrating Guava's optional class.
    * 
    * @param arguments Command-line arguments; none expected.
    */
   public static void main(final String[] arguments)
   {
      final GuavaOptional me = new GuavaOptional();

      final String wyoming = "Wyoming";
      final Optional<String> wyomingCapitalWrapper = me.getStateCapital(wyoming);
      if (wyomingCapitalWrapper.isPresent())
      {
         out.println("Capital of " + wyoming + " is " + wyomingCapitalWrapper.get());
      }
      out.println("Capital of " + wyoming + " is " + wyomingCapitalWrapper.orNull());

      final String northDakota = "North Dakota";
      final Optional<String> northDakotaCapitalWrapper = me.getStateCapital(northDakota);
      out.println("Capital of " + northDakota + " is " + northDakotaCapitalWrapper);
      out.println("Capital of " + northDakota + " is " + northDakotaCapitalWrapper.or("Unspecified"));
      out.println("Capital of " + northDakota + " is " + northDakotaCapitalWrapper.orNull());

      final Optional<String> nullOptional = me.getStateCapital(null);
      out.println("Capital of null is " + nullOptional);
      

      final BigDecimal dividend = new BigDecimal("5.0");
      final BigDecimal divisor = new BigDecimal("0.0");
      final Optional<BigDecimal> quotientWrapper = me.getQuotient(dividend, divisor);
      out.println(  "Quotient of " + dividend + " / " + divisor + " is "
                  + quotientWrapper);
   }
}

Proper use of Guava's Optional class can reduce NullPointerExceptions (NPEs) and increase the expressiveness of return values. However, even using Optional does not mean the end of exceptions. For example, passing a null as the parameter to the static method Optional.of(T) when attempting to obtain an instance of Optional leads to an NPE. This is not too surprising given that the method's documentation states, "Returns an Optional instance containing the given non-null reference." However, the Javadoc for the method does not explicitly state that this is thrown. It is interesting that this condition is detected using Guava's own Preconditions class. Another exception that can occur is IllegalStateException when the Optional.get() method is called on an instance of Optional that does not have a non-null reference contained within it. This method's Javadoc documentation does state conditions under which this one can be thrown.

Guava seems to specialize in providing common functionality and utilities many of us develop for our own code bases. The advantages of using Guava's classes include being able to use classes that Java developers outside a given organization have a chance to know about as well as the advantages of using products supported by open source communities. Guava's Optional provides a convenient and easy-to-use mechanism for adding safety and expressiveness to a code base. There are still situations where I prefer a more specific type of null object (the object represents either null or a particular non-null type), but Optional is nice for those situations where I need a general solution because I cannot justify the cost of creating a specific solution.

 

From http://marxsoftware.blogspot.com/2011/10/guavas-new-optional-class.html

Published at DZone with permission of Dustin Marx, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Erwin Mueller replied on Fri, 2011/12/30 - 4:56am

What a sad day for software developer. Not only you totally misused Optional, but you telling everyone to use very bad programming habits.

First of all, how is this makes your code more saver:

 

if (wyomingCapitalWrapper.isPresent())
{ out.println("Capital of " + wyoming + " is " + wyomingCapitalWrapper.get());
}

What is the difference with this?

if (wyomingCapital != null)
{ out.println("Capital of " + wyoming + " is " + wyomingCapital);
}

There is no difference, other then you make your code more complicated. wyomingCapitalWrapper.get() will throw you a NPE anyway, so you have to test it first.

The whole point of a NPE is to signal that there is a bug in your code. A function should return something, that's the whole point of a function. If a function can not return anything, then that's (literally) an exception. There is no point in using anything fancy here, and it makes your application less saver if you do.

 

public String getStateCapital(final String stateName) {
if (!stateCapitals.containsKey(stateName)) { 
throw new IllegalStateException("State not found in the database");
}
return stateCapitals.get(stateName);
}

If you try to hide the fact that the function can't return anything, then the bug is just hidden, and will brake your application in some non- obvious way. For example, the user will now see "null" as the state capital.

Furthermore, it is the responsibility of the class to have default values, not the responsibility of the caller. If you have the caller to set the default values, then every caller will set different default values, and it is all over your code. You should set the default value in the class which is responsible for the data.

 

public String getStateCapital(final String stateName) {
if (!stateCapitals.containsKey(stateName)) { 
return "Unspecified";
}
return stateCapitals.get(stateName);
}

Also, a plus point for catching the Exception class, which you should never do. A division against 0 is a bug, your application should never do that. You should inspect the user input and inform the user that your application cannot divide against 0. In your code now we have a bunch of Optional-objects with the value of null. Try to do something useful with a bunch of null-references from now on.

On a site note: Now you not only have to deal with NPE, but with the IllegalStateException from Optional.get(). Nice way to make your application saver.

In your examples, all you did is to hide bugs in your code in the Optional-wrapper. Now the application is not just exit with a NPE (or with other runtime exceptions), but it will run for hours with invalid data. How is this suppose to make your application saver? Good for the user, who is wondering what's happening and sees nothing suspicions in the logs.

Yes, I agree, a method should never return null. It should always return something, and if a method can't return something, it should throw an exception.

PS: I wish the Java developer had make it so, that a null-reference in a String would throw a NPE. Like in "Foo string with null reference " + reference + ".". Here it should throw NPE if reference is null.

PSS: To your Effective Java NullPointerException Handling. You really do catch NPE? WTF? A NPE is not an Error, it's a bug in your code. A NPE should never be encounted, either by checking user input, checking data availability, etc.

Arnaud Des_vosges replied on Fri, 2011/12/30 - 5:39am

@Nullable + FindBugs can help to detect NPE.

Ant Kutschera replied on Fri, 2011/12/30 - 8:40am in response to: Arnaud Des_vosges

@Arnaud: is @Nullable IDEA specific?

Ant Kutschera replied on Fri, 2011/12/30 - 8:43am

@Erwin: I tend to agree.  I wonder if Google added this to Guava, because it exists in Scala (http://www.scala-lang.org/api/current/scala/Option$.html), where the language also supports it?

From Guava's Docs:

Some uses of this class (Optional) include

  • As a method return type, as an alternative to returning null to indicate that no value was available
  • To distinguish between "unknown" (for example, not present in a map) and "known to have no value" (present in the map, with value Optional.absent())
  • To wrap nullable references for storage in a collection that does not support null (though there are several other approaches to this that should be considered first)

 

I'd say the second and third points make more sense, and it would have been good for this article to give examples of them too.

Andy Jefferson replied on Fri, 2011/12/30 - 9:08am in response to: Ant Kutschera

Sounds more like javax.validation.Nullable actually, so a standard.

Dmitriy Milashenko replied on Fri, 2011/12/30 - 9:21am in response to: Ant Kutschera

@Ant

I wonder if Google added this to Guava, because...

 

I assume they added this to let us have code like:

queryCache(query).or(queryDirectly(query)) 
....
static <R> Optional<R> queryCache(Query<R> q) {
       return ....;
}
static <R> Supplier<R> queryDirectly(final Query<R> q) {
        return new Supplier<R>(){
            @Override
            public R get() {
                return q.fetch();
            }
        };
}

 

Arnaud Des_vosges replied on Fri, 2011/12/30 - 2:49pm in response to: Ant Kutschera

I use FindBugs plugin in Eclipse.

It supports JSR 305 annotations (e.g. javax.annotation.Nullable).

André Pankraz replied on Fri, 2011/12/30 - 7:41pm

@Erwin a little bit harsh...I like such posts way more than many metaphorial or IT astrology stuff I see each day, even if I don't aggree. At least you can discuss such stuff.

I don't see much value in

if (wyomingCapitalWrapper.isPresent())
{ out.println("Capital of " + wyoming + " is " + wyomingCapitalWrapper.get());
}

either if the compiler accepts this get() without enforcing a previous isPresent(), but there are alternative concepts that work in a similar way.

Look at Ceylons "String | Nothing", or String? - the typechecker and the compiler enforce a similar structure like the above and they prevent NPEs with that.

This Guava Optional class will not bring this to live in Java and I don't like it. FindBugs is way better for this.

John J. Franey replied on Sat, 2011/12/31 - 2:21pm

Well, the OP did not propose that using Optional excludes the need to use Findbugs. Findbugs and Optional ought not be presented as an either/or proposition. Findbugs could detect the error of null Optional references (e.g., wyomingCapitalWrapper == null).

Aren't there limitations to the static analysis approach? Are there some null pointer bugs that Findbugs doesn't find? Although it is very effective, is it the only technique to improve quality?

@Erwin makes good points against use of Optional, but I don't think an absolutist position is helpful. (Never say 'never'!). Not all software programs need to stop short with an NPE and wait until a fix arrives from the developer. Some programs must continue to operate in spite of these errors. Also, a user's confidence in the software drops more quickly with NPE in the log, than it would with strange data on the screen.

In other words, not all null pointer references will be detected by static analysis. All developers should have some strategy for handling NPE's that happen. For some cases, a dead program with an NPE in the log is the right answer, but not all cases.

Finally, I'm grateful to the OP for writing up the Optional class here...Its the first I've heard about it.

Erwin Mueller replied on Sun, 2012/01/01 - 10:18am

Sorry, maybe it was too harsh. But I just can't see any point in preventing a NPE. As I have stated before, a NPE, like any runtime exception, is a Bug in the code and should be treated as such. We have NPE for the reason so we can avoid memory corruption, and all the security nightmares of the C programs.

What are you going to do after a NPE? If you don't catch it, the program will exit and the bug will be obvious, so the developer can fix it. If you catch it, then the program will still run, but will be broken in some way.

I had such NPE with VirtualParadigm. After an update, I couldn't choose the color or the font any more. A look at the logs I saw a NPE been thrown. How could this be happening? I would say, because the developers decided to catch the NPE and just log it away.

I don't think they have test cases to test the log for any NPE.  If they didn't catch it and log it away (i.e. hide), the bug would be obvious and would not be happened in the production version.

The people are always like, if it's not stabs you in the eye, you will forget it. An NPE should stub you in the eye, it's very critical bug.

 IMHO it was a big mistake from Sun to make runtime exception catch-able. The checked/unchecked exceptions paradigm is IMHO a very good idea, only if they named it different: runtime bug and (normal) exception would've been better names. With the addition that you can't catch runtime bugs.

 

@Andy Jefferson

 

I still don't see the point. Why is not   queryCache() just call queryDirectly() if the value is not in the cache? The whole point in a cache is to be a transparent proxy. The caller should not known if she gets the value from a cache or from the source.

The only use case for the Option I see as a optional parameter for a method, with a default value. Something like:

void setGraphicCard(Optional cardName) {
String name = cardName.get(); // returns the set name, or a default one
}

 

Ant Kutschera replied on Thu, 2012/01/12 - 4:13pm

I've been reading up, and it all becomes clear - the Optional class is related to functional programming and Guava's support for it. It isn't really intended for use as documented here. See the following resources: http://kerflyn.wordpress.com/tag/functional-programming/
http://learnyouahaskell.com/chapters
http://shop.oreilly.com/product/0636920021667.do

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.