Nulls
Doug Lea
dl at cs.oswego.edu
Sat Sep 22 05:00:07 PDT 2012
One more push for the what I still think is the most defensible
(and simplest to explain) set of design rules:
1. Stream operations ignore null elements.
2. Each operation that can return a "nothing there" result has two forms
Optional<T> op(...)
T op(..., T defaultValueIfNone);
Notes:
Rule (1) means that stream.forEach(action) must be implemented as
x = getNextElement(); if (x != null) action.apply(x)
and so on. As I mentioned, it would arguably be better to require
that Streams themselves never produce nulls, but this can't be done
without losing the ability to rely on iterators for existing collections.
The only argument I know against this rule is that it can have the
effect of delaying any consequences of using an element that should
have been nonnull but was null due to a programming error.
I'm sympathetic, but I think that burdening Streams with
this is misdirected: Early detection of such errors is one
reason why (dense) collections themselves shouldn't allow nulls.
If people have chosen to use collections allowing nulls,
they have already made a choice about this.
Rule (1) removes the ambiguity of an Optional with value null.
(And enables the spec for Optional to say that a present
optional is never null.)
Rule (2) enables fluent styles without requiring them.
This reflects the fact an "Optional" type is required
only in languages that support "value types" that can never be null.
This includes Java, but only for primitive types. However,
some types (like String) act so much like value types that
using this style is appropriate. As an unrelated byproduct,
Optional supports more fluent expression-y style that some people
love so much they cannot otherwise cope, and others want to
at least sometimes use. My guess is that once some of the
newness of fluency wears off, most people will be in the
second group, so will want multiple options.
The other (default arg) method form applies in contexts where null
(or here, extended to arbitrary default values) returns have
their traditional meanings without forcing an unneeded
second level of wrapping. This in part reflects the fact that
Optional and boxing are essentially the same idea, and so
an optional around a box is just pure wasted overhead that
conscientious developers may wish to avoid. (During Java5
development, some people thought that boxing overheads weren't
important enough to provide systematic alternatives to. As it
turns out, they were very wrong. We can at least profit
from the lessons learned here.)
Finally, among the best arguments for these rules is that they
apply equally well to value types (primitives, plus any future
compound value types). So any API conforming to them has a chance
of being specialized in its entirely to, say, streams of doubles.
Although one remaining messy part is that "Optional<double>"
(if such a thing were legal) is basically an alias for
existing class Double, and there seems to be no reasonable
way to force them to be the same nominal type. My Numerics posts
address one way to reduce impact, but I still don't see a
general backward compatible solution.
-Doug
More information about the lambda-libs-spec-observers
mailing list