RFR(m): 8177290 add copy factory methods for unmodifiable List, Set, Map

John Rose john.r.rose at oracle.com
Sun Nov 19 03:34:12 UTC 2017

On Oct 30, 2017, at 6:50 PM, Stuart Marks <stuart.marks at oracle.com> wrote:
> (also includes 8184690: add Collectors for collecting into unmodifiable List, Set, and Map)

Now I'm going to be picky about the names of the Collectors;
please bear with me a moment.  Consider `toUnmodifiableList`
and its two cousins (`toUSet`, `toUMap`).

The most natural names `toList` (etc.) are already taken.
They mean "do whatever is most useful for the collector,
perhaps returning unmodifiable or modifiable results".

So the problem here is giving the user the option to say,
"no, I really want a value-based-class-style list here; I know
you can do this for me".

`Collectors.toList` can't ever be upgraded in place, since
the programmer is getting a Collector's Choice result,
and programmers will be inadvertently relying on whatever
that (undocumented) choice happens to be.

I think this makes the name `toList` significantly less useful,
and I for one will never want to use `toList` again, once
`toUnmodifiableList` is available.

Meanwhile, `toUnmodifiableList` is so ugly to the ear,
eye, and fingers that it will be less used just because of
its long spelling.

Users should be allowed to code as if unmodifiability is a
common practice, not something that must be re-announced
every time I make a new temporary list.  That's why `copyOf`
and not `unmodifiableCopyOf` is the right move, and
`toUnmodifiableList` is not.

(See also Gafter's blog[1] on making NEW FEATURES look NEW.
The NEW UNMODIFIABILITY will soon cease to be new but in
its present form it will shout forever like an aging rocker.)

To get out of this bind, I suggest picking a shorter name that
emphasizes that the result is the _elements_ in the list, not the
list object per se.  This is a VBC[2] move, one which we need to
learn to make more often, especially as value types arrive.
So I suggest these names for the collectors:  `elements` (for
a list), `elementSet`, and `elementMap`.  Same signatures,
of course.

Another approach to this, which recycles the old names but
doesn't have the VBC vibe, is to create a second set of
factories for list/set/map collectors that look just like the
old ones, but behave more predictably.

For example, one might try to add methods to `Collector`
named `asModifiable` and `asUnmodifiable`, so people can
write `toList().asModifiable()`.  I don't think this works, nor
is it very pretty (looks too "NEW"), although it provides a
more principled fix to the Collector's Choice problem.

If we go forward with the change as written, I think people will
only want to remember the one `toList` and forget about his ugly
brother.  Only zealots like me will remember to add the extra
incantation ("NEW!  now Unmodifiable!").

Another, better way to recycle the name `toList` is to provide
`Collectors.Modifiable` and `Collectors.Unmodifiable`, and then
have users import `Collectors.Unmodifiable.toList`.  This at least
would move the shouting up to the static imports list and out of
real code.

But I think `toList` is broken and should be avoided.  So I
think it should be `elements`, `elementSet`, and `elementMap`.

Even if you think that unmodifiability should be mentioned every
time a collector collects some stuff, observe that `toUnmodifiableList`
is not a strong parallel with the older API point, `Collections.unmodifiableList`.
That guy creates a _view_ while `copyOf` and `elements` (my name)
create non-view unmodifiable (VBC, shallowly immutable) lists.
So `Collectors.toUnmodifiableList` is both ugly and ill-sorted with `copyOf`.

— John

[1]: http://gafter.blogspot.com/2017/06/making-new-language-features-stand-out.html
[2]: https://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html

More information about the core-libs-dev mailing list