Platinum Partner
java

Guava's New Optional Class

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

{{ tag }}, {{tag}},

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

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}