java.util.Optional fields

Peter Levart peter.levart at gmail.com
Fri Sep 21 06:44:07 PDT 2012


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