java.util.Optional fields

Lucas Cavalcanti lucas at cavalcanti.me
Fri Sep 21 08:21:14 PDT 2012


If you call .get() without checking you're doing it wrong. There's no way
to ensure this won't happen.

But if the type is Optional<> I know I should write code for the absent
value. If the type is not optional, there is no way to know if I should
check for null.
So this is the biggest value.

if I have to do:

if (optional.isPresent()) {
   Object obj = optional.get();
   if (obj ==  null) {
        throw something;
   }
   ...
}

there is no point of having an optional type.

On Fri, Sep 21, 2012 at 12:05 PM, Peter Levart <peter.levart at gmail.com>wrote:

>  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>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