java.util.Optional fields

Peter Levart peter.levart at gmail.com
Fri Sep 21 08:05:04 PDT 2012


On 09/21/2012 04:42 PM, Lucas Cavalcanti wrote:
> 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.

If you don't check Optional (.isPresent()) and blindly call .get() you 
will get NoSuchElementException which is no different to having no 
Optional class and just returning null and checking for null in such cases.

Besides fluent API offered by Optional, your proposed Optional has not 
value over null.

The 3-state Optional (non-null value, null value, empty) has a benefit 
in that it can express the 3rd option in a way that does not force using 
exceptions.

Regards, Peter

>
> Regards,
> Lucas
> On Fri, Sep 21, 2012 at 10:44 AM, Peter Levart <peter.levart at gmail.com 
> <mailto: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>
>     > <mailto: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>
>     <mailto: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>
>     >     <mailto: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