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