java.util.Optional fields

Lucas Cavalcanti lucas at cavalcanti.me
Fri Sep 21 07:42:11 PDT 2012


The point of having an Optional type is not having to care about null
pointer exceptions anymore.

Therefore this class should NOT allow null as a valid present value. null
is an absent value.

Regards,
Lucas
On Fri, Sep 21, 2012 at 10:44 AM, Peter Levart <peter.levart at gmail.com>wrote:

> I actually like Optional as a name, especially because it has additional
> targeted API that simplifies logic by allowing calling it's methods in a
> chain...
>
> When I see where it's used in Stream, for example:
>
>      Optional<T> findFirst();
>
> I can only imagine that it's purpose was to allow null values as
> elements of streams. That is, it allows to differentiate between a
> not-present (elements of a stream) and an element of 'null'...
>
> An alternative of throwing NoSuchElementException was replaced by
> returning an Optional.
>
> Regards, Peter
>
> P.S.
>
> As to implementation details, here's a way to implement Optional in a
> single final class and not having additional boolean flag:
>
>
> public final class Optional<T> implements Serializable {
>
>      private static long serialVersionUID = 1L;
>
>      /**
>       * Common instance for {@code empty()}.
>       */
>      private final static Optional<?> EMPTY = new Optional<>(null);
>
>      /**
>       * Value, if present.
>       */
>      private final T value;
>
>      public Optional(T value) {
>          this.value = value;
>      }
>
>      /**
>       * An empty object.
>       *
>       * Note: Though it may be tempting to do so, avoid testing if an
> object
>       * is empty by comparing with {@code ==} against instances returned
>       * {@code Option.empty()}. There is no guarantee that it is a
> singleton.
>       *
>       * @param <T> Type of the non-existent value.
>       * @return an empty object.
>       */
>      public static<T> Optional<T> empty() {
>          return (Optional<T>) EMPTY;
>      }
>
>      /**
>       * Returns the value of this object.
>       *
>       * @return the value of this object.
>       * @throws NoSuchElementException if there is no value present.
>       */
>      public T get() {
>          if (this == EMPTY) {
>              throw new NoSuchElementException("No value present");
>          }
>          return value;
>      }
>
>      /**
>       * Return {@code true} if there is a value present otherwise {@code
> false}.
>       * @return {@code true} if there is a value present otherwise
> {@code false}.
>       */
>      public boolean isPresent() {
>          return this != EMPTY;
>      }
>
>      /**
>       * Return the value if present otherwise return {@code other}.
>       *
>       * @param other value to be returned if there is no value present.
>       * @return the value if present otherwise return {@code other}.
>       */
>      public T orElse(T other) {
>          return this != EMPTY ? value : other;
>      }
>
>      /**
>       * Return the value if present otherwise return result of {@code
> other}.
>       *
>       * @param other Factory who's result is returned if there is no
> value present.
>       * @return the value if present otherwise return result of {@code
> other}.
>       */
>      public T orElse(Factory<T> other) {
>          return this != EMPTY ? value : other.make();
>      }
>
>      /**
>       * Return the value otherwise throw an exception to be created by the
>       * provided factory.
>       *
>       * @param <V> Type of the exception to be thrown.
>       * @param exceptionFactory The factory which will return the
> exception to
>       * be thrown.
>       * @return the value.
>       * @throws V if there is no value present.
>       */
>      public<V extends Throwable> T orElseThrow(Factory<V>
> exceptionFactory) throws V {
>          if (this != EMPTY) {
>              return value;
>          } else {
>              throw exceptionFactory.make();
>          }
>      }
>
>      /**
>       * Return the value otherwise throw an exception of the provided
> class.
>       * Exception will be thrown with the message "No value present".
>       *
>       * @param <V> Type of the exception to be thrown.
>       * @param exceptionClass The class if exception to be thrown. Must
> support
>       * the default zero arguments constructor.
>       * @return the value.
>       * @throws V if there is no value present.
>       */
>      public<V extends Throwable> T orElseThrow(Class<V> exceptionClass)
> throws V {
>          if (this != EMPTY) {
>              return value;
>          } else {
>              try {
>                  throw exceptionClass.newInstance();
>              } catch (InstantiationException | IllegalAccessException e) {
>                  throw new IllegalStateException("Unexpected exception
> attempting to throw " + exceptionClass, e);
>              }
>          }
>      }
>
>      public<V> Optional<V> map(Mapper<T, V> mapper) {
>          return this != EMPTY ? new Optional<>(mapper.map(value)) :
> Optional.<V>empty();
>      }
>
>      @Override
>      public boolean equals(Object o) {
>          if (this == o) {
>              return true;
>          }
>          if (this == EMPTY || o == EMPTY || o == null || Optional.class
> != o.getClass()) {
>              return false;
>          }
>
>          return Objects.equals(value, ((Optional)o).value);
>      }
>
>      @Override
>      public int hashCode() {
>          int result = Objects.hashCode(value);
>          result = 31 * result + (this != EMPTY ? 1 : 0);
>          return result;
>      }
>
>      @Override
>      public String toString() {
>          return this != EMPTY ? String.format("Optional[%s]", value) :
> "Optional.empty";
>      }
>
>      // Serialization
>
>      private Object writeReplace() throws ObjectStreamException {
>          if (this == EMPTY) {
>              return EmptyProxy.INSTANCE;
>          }
>          else {
>              return this;
>          }
>      }
>
>      private static final class EmptyProxy implements Serializable {
>          private static final EmptyProxy INSTANCE = new EmptyProxy();
>          private Object readResolve() throws ObjectStreamException {
>              return EMPTY;
>          }
>      }
> }
>
>
>
>
>
> On 09/21/2012 03:11 PM, Vitaly Davidovich wrote:
> >
> > I disagree, as I mentioned on that other thread as well. :) A lot of
> > types can be considered as containers in the abstract - that's not a
> > very interesting distinction.  In this case, a library class is added
> > to make a language feature a bit less error prone (i.e. null and
> > associated NPEs that unsuspecting developers hit).  Given this,
> > Optional sounds correct and more targeted.  Moreover, given that
> > Optional is meant to replace use of null, I don't think it should
> > allow null as a valid value. If null and "absent" have different
> > meaning in a given scenario then don't use Optional for those.
> >
> > Sent from my phone
> >
> > On Sep 21, 2012 9:00 AM, "Paul Benedict" <pbenedict at apache.org
> > <mailto:pbenedict at apache.org>> wrote:
> >
> >     > On Fri, Sep 21, 2012 at 7:19 AM, Vitaly Davidovich
> >     <vitalyd at gmail.com <mailto:vitalyd at gmail.com>> wrote:
> >     > Why not implement this like Guava with two concrete subtypes of
> >     Optional:
> >     > Present and Absent? It seems cleaner and I don't think
> >     performance will be
> >     > worse as compiler will only ever see two possible receivers and
> >     can use a
> >     > PIC to eliminate calls via vtable in those cases.
> >
> >     I think this thread touches on an email I wrote earlier, which is
> >     Optional is not really a good name choice. It's all focusing on
> >     its potential lack of value rather than just being what it is -- a
> >     container.
> >
> >     > On Sep 21, 2012 7:42 AM, "Peter Levart" <peter.levart at gmail.com
> >     <mailto:peter.levart at gmail.com>> wrote:
> >     > Maybe the intention was to allow null values to be wrapped in
> >     non-empty
> >     > Optional? In that case the check for non-null in constructor is
> >     wrong...
> >
> >     Peter, I agree that a present value of null and an absent value
> >     (defaults to null) need to be differentiated. Yes, it looks wrong.
> >
> >     Paul
> >
>
>
>
>


More information about the lambda-dev mailing list