Статьи

Новый дополнительный класс в Гуаве

Guava Release 10 представляет класс Optional , который можно использовать там, где можно использовать нулевой объект . Я уже создавал свои собственные классы, подобные этому, но преимущество Guava в том, что его можно легко использовать в разных проектах, и разработчики, не знакомые с организацией, могут знать об этом уже, если они используют Guava. Кроме того, использование класса Guava выгодно по сравнению с использованием собственного аналогичного или нулевого класса объектов из-за обычных преимуществ, связанных с сообществами с открытым исходным кодом, таких как больше тестирования и больше внимания к коду.

Как я писал в посте Effective Java NullPointerException Handling , я обычно не хочу, чтобы методы возвращали null. С помощью методов, которые возвращают коллекции, возврат пустой коллекции является простым решением. Однако когда методы возвращают произвольные объекты, не всегда легко указать «пустое» условие. Необязательный класс Guava может удовлетворить эту потребность.

Класс Optional не имеет конструкторов, но предоставляет три открытых статических метода для получения экземпляра класса. Optional.fromNullable (T) позволяет предоставлять нулевую или ненулевую ссылку и переносить ее в новый экземпляр Optional. Если переданный параметр имеет значение null, то экземпляр не имеет сохраненной ссылки и является «отсутствующим» экземпляром. Если переданный параметр не равен NULL, эта ненулевая ссылка сохраняется в новом дополнительном экземпляре.

Другой общедоступный статический метод для получения экземпляра Optional — это Optional.of (T) . Этот метод действует как Optional.fromNullable (T) за исключением того, что он ожидает, что ему будет передан ненулевой параметр. Если ему передается значение null, генерируется исключение NullPointerException.

Третий статический метод получения экземпляра Optional — это Optional.absent () . Это полезно, когда есть код, который знает, что параметр, который был бы предоставлен для Optional.fromNullable (T), имеет значение null, и это более ясно выразить, чем должна быть возвращена «отсутствующая» версия Optional.

Как только экземпляр Optional был получен (например, возвращен методом), существует несколько методов экземпляра, которые можно вызвать в этом экземпляре. Метод Optional.isPresent () полезен для определения того, имеет ли данный необязательный экземпляр ненулевой параметр.

Как только становится известно (например, путем вызова Optional.isPresent ()), что экземпляр Optional содержит ненулевую ссылку, метод Optional.get () возвращает эту сохраненную ненулевую ссылку. Обратите внимание, что если нет ненулевой ссылки, исключение выдается при вызове этого метода, поэтому рекомендуется сначала вызвать isPresent ().

Существует несколько перегруженных методов или методов, которые позволяют указывать значения по умолчанию, которые возвращаются, когда экземпляр Optional не содержит собственную ненулевую ссылку. Эти методы предоставляют хороший способ указать значение по умолчанию, которое будет возвращено, если оно будет возвращено в виде нуля, в противном случае (или будет сгенерировано исключение). Могут возникнуть ситуации, особенно при взаимодействии с уже существующими API, в которых желательно возвращать нулевой, а не «отсутствующий» необязательный экземпляр. Это легко сделать с помощью Optional.orNull (), который возвращает либо ссылочный тип, который содержит экземпляр, либо возвращает null, если он не содержит ненулевую ссылку.

Многие из концепций, обсужденных выше, иллюстрируются в следующем листинге кода.

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);
   }
}

Правильное использование необязательного класса Guava может уменьшить количество исключений NullPointerException (NPE) и повысить выразительность возвращаемых значений. Однако даже использование Optional не означает конец исключений. Например, передача нулевого значения в качестве параметра статическому методу Optional.of (T) при попытке получить экземпляр Optional приводит к NPE. Это не слишком удивительно, учитывая, что документация метода гласит: «Возвращает необязательный экземпляр, содержащий данную ненулевую ссылку». Однако в Javadoc для этого метода явно не указано, что он выброшен. Интересно, что это условие обнаруживается с использованием собственного класса предварительных условий Guava . Еще одно исключение, которое может возникнуть, — IllegalStateException, когдаМетод Optional.get () вызывается для экземпляра Optional, в котором нет ненулевой ссылки. В документации Javadoc для этого метода указываются условия, при которых этот метод может быть выдан.

Guava, кажется, специализируется на предоставлении общих функций и утилит, которые многие из нас разрабатывают для наших собственных баз кода. Преимущества использования классов Guava включают возможность использовать классы, о которых разработчики Java могут узнать за пределами данной организации, а также преимущества использования продуктов, поддерживаемых сообществами с открытым исходным кодом. Опционально Guava предоставляет удобный и простой в использовании механизм для повышения безопасности и выразительности базы кода. Есть еще ситуации, когда я предпочитаю более конкретный тип нулевого объекта (объект представляет либо нулевой, либо конкретный ненулевой тип), но Optional подходит для тех ситуаций, когда мне нужно общее решение, потому что я не могу оправдать стоимость создания конкретное решение.

 

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