Stream proposal and null results
Brian Goetz
brian.goetz at oracle.com
Fri May 11 10:55:20 PDT 2012
Yes, you caught us :)
Currently getFirst() is the only method that cheats in this way (using
null as a sentinel), and this is generally a step in the wrong
direction. A much more promising direction here would be to do
something like Option (Guava) or Optional (Scala) where getFirst returns
an Option, rendering it impossible for the user to "forget" to do the
null check. And, of course, having the return type explicitly represent
maybe-null is more self-documenting than the current prototyped
approach. Moving to some sort of Option-like approach here is on our
to-do list, but currently we're working through some bigger issues
first, such as how the streams framework attaches to collections.
A secondary benefit of this approach is that it eliminates a "garbage"
variable from the client code. Such "garbage" variables can often
obfuscate the flow of what's going on.
So, total agreement; we just haven't gotten around to this yet.
On 5/11/2012 1:06 PM, Scott Carey wrote:
> In the stream API draft proposal, I noticed that null was used to signify
> a failed computation or filter:
>
> Method matching =
> Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
> .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
> .filter(m -> Arrays.equals(m.getParameterTypes(),
> parameterClasses))
> .filter(m -> Objects.equals(m.getReturnType(), returnType))
> .getFirst();
> if (matching == null)
> throw new InternalError("Enclosing method not found");
> return matching;
>
>
> It would be very useful to avoid requiring clients to check the result for
> null. This error prone null check pattern can be avoided and pushed into
> the framework with something like:
>
>
> return
> Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
> .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
> .filter(m -> Arrays.equals(m.getParameterTypes(),
> parameterClasses))
> .filter(m -> Objects.equals(m.getReturnType(), returnType))
> .getFirstOrElse(() -> throw new InternalError("Enclosing method
> not found"));
>
>
> A couple other signatures for the last line are very useful as well:
>
> .getFirstOrElse(() -> someDefaultComputation()));
>
>
> .getFirstOrElse(someDefaultValue));
>
>
>
>
> If the only API contract for a computation with no result is to return
> null, then the pattern
>
> T tempVar = values.someComputations().getResult();
> if (null == tempVar) {
> // do something about no result here
> }
> return tempVar;
>
> will be extremely common boilerplate that can be avoided .
>
> I think there are a three signatures that together remove the boilerplate.
> Below I have changed the name from getFirstOrElse to getFirst:
>
> void getFirst(Runnable elseAction)
>
> T getFirst(Callable<T> elseResult)
> T getFirst(T elseVal)
>
> T getFirst() is then simply a shortcut for
>
> T getFirst((T)null);
>
>
>
>
> There is likely something more elegant than multiplying the number of
> method signatures for every method that has an optional result.
>
>
>
More information about the lambda-dev
mailing list