Optional

Remi Forax forax at univ-mlv.fr
Fri Sep 14 15:14:16 PDT 2012


On 09/14/2012 05:14 PM, Brian Goetz wrote:
> On 9/14/2012 1:23 AM, Sam Pullara wrote:
>
>> 2) Optional should implement more of the Stream API like flatMap and
>> some others.
>
> This is a reasonable option.  Currently, the Optional class is a 
> strawman, focusing on the methods needed for dealing with absence and 
> omitting these convenience methods.  Happy to consider this.

If optional implements Stream or StreamOps then you will see 
List<Optional<T>> and likes because you allow to delay the check to now 
if the optional value exist or not.

>
>> I also hate that with Optional.none() often needs to be witnessed and
>> not a big fan of using a constructor for Optional:
>
> This is a problem not with Optional, but with type inference.  In Java 
> 7 we do very limited type inference of generic methods in nested 
> method call contexts.  This should improve.
>
>
> Remi wrote:
>> Is there a document somewhere that explain the pro and cons of using 
>> Optional ?
>
> The pros are simple: there are eager stream methods that may return 
> nothing.  (Lazy methods like filter can return an empty stream.)  The 
> "obvious" way to deal with this is to return null. But this has 
> multiple disadvantages:
>  - If null is a valid value, you're hosed just like when map.get(key) 
> returns null; you can't tell the difference between "no mapping" and 
> "mapping with value=null".

yes, it's not a perfect solution but may be people will stock to put 
null in collections or streams.

>  - People forget to do the null check, and get NPEs.  Explicit 
> optional doesn't have this risk; the type system saves you from yourself.

in that cas @Nullable/@NonNull are better than Optional because they are 
pure type without any runtime overhead
and can be integrated to the type system a la Kotlin.

>  - For primitive-valued streams, we can't even coopt null here.

You can not use Optional of T too.

>  - Optional provides a means of doing the null check in a fluent 
> manner.  Compare:
>
>   T result = collection.stream()
>                        .filter(...)
>                        .map(...)
>                        .findFirst();
>   if (result == null) {
>       throw new NoSuchFooException();
>   }
>   return result;
>
> and
>
>   return collection.stream()
>                    .filter(...)
>                    .map(...)
>                    .findFirst()
>                    .getOrThrow(() -> new NoSuchFooException());

you have also to compare to:

return collection.filter(...)
                    .map(...)
                    .findFirstOr(() -> new NoSuchFooException());

>
> Many people like to harp on the performance issues here, but I think 
> those are red herrings.  If you look at the use of Optional in this 
> API, it shows up in exactly one situation: at the end of a bulk 
> operation that might yield no results.  There is no List<Optional<T>> 
> anywhere, there is no O(n) Optional-boxing anywhere.  It is a small 
> O(1) overhead which gives a significant improvement in safety and 
> expressiveness. This tradeoff is totally worth it.
>
> (It may even be that the VM can eliminate the Optional box someday via 
> box elimination, at which point the performance cost is zero.)

My main concern is that theoretically the VM should remove allocation of 
Optional because of escape analysis,
but practically it will not occur because escape analysis works if you 
can inline the caller of findFirst() with the code
of findFirst() and because the code of findFirst() is too big to be 
duplicated, the allocation is not removed in practice.

Brian and Tim, I agree with you that introducing Optional for 
findFirst/findAny is harmless,
So if you can guarantee me that I will never have to fix perf bugs in a 
program that use Optional as return value of a hashmap or a cache, I 
will vote for Optional*.

Rémi
* otherwise I propose to rename it as PandoraBox because it's a special 
kind of boxing :)

>
>
> David wrote:
>
>> I don't know - to me Optional<T> is Pair<A, B>'s brother.  Both are
>> useful, in their way, but both have potential to massively stink up
>> code.  I don't really believe the benefits are worth it - I mean the
>> best improvement we get isn't an objective "it's faster" or "it
>> allows more optimal code paths", it's purely a style thing and it
>> does have a cost.  I don't like it; I think it's going to result in
>> things like:
>>
>> Map<String,Optional<List<Optional<String>>>>
>
> Yes, if you invent fire people will burn themselves.  But, I'm with 
> Tim on this one.  In the cases where we've used it, it is just the 
> right thing, and food tastes better and is safer when cooked.
>
>
> Doug wrote:
>
>> But it may be worth breaking some consistency for
>> the sake of usability in supplying a few such choices
>> in Stream API. In particular, findAny
>>   Optional<T> findAny();
>>   T findAny(T ifNone);
>
> These are certainly easy enough to implement, and might carry their 
> weight if "use a default value" were the dominant fallback action.  Is 
> it?  Or is throwing just as common / more common?  Tim and Sam, what's 
> your experience here?



More information about the lambda-libs-spec-observers mailing list