ArrayFactory SAM type / toArray
brian.goetz at oracle.com
Wed Sep 19 15:13:43 PDT 2012
> I don't find these arguments convincing. There's no race (any more than
> there is for any bulk operation) as the allocation is done by the object
> itself. The allocation stuff is pretty much a red herring: most users
> don't preallocate the array. So it seems to me that using factories here
> might amount to needless complexity and inconsistency.
I agree with you that most users don't pre-allocate the array. Which
makes the existing form of toArray even more unfortunate! Because then
the allocation always involves multiple reflective calls. (Some of
which are sometimes optimized by some VMs in some conditions, but none
of which are always optimized by all VMs in all conditions.) So the
performance will always be worse in the toArray(T) formulation.
The fundamental problem is that the client knows how best to create the
array (the client can say "new Foo" but the library cannot say "new T",
and therefore has to fall back to reflection), but the library knows
best how big the array should be.
This is a classic example of the sort of differences in APIs you get
when designing an API with or without closures. The client knows how;
the library knows how much; ideally we'd like for the client to pass
that knowledge into the library. The approximations we got when it is
hard to combine these are unfortunate; we can do better now.
I'd find David's suggestion of toArray(Class) more compelling (in some
sense it is the most "right" in that it doesn't conflate "what" with
"how") except I don't buy that the intrinsification of reflective array
allocation in some VMs in some compilation modes in some situations
makes all the reflective costs go away.
We're creating a new API here. All things being equal, we should lean
on consistency with existing APIs when we can, but obviously that is
just a guideline (someday we're going to have to contend with the fact
that an int isn't big enough to store the size of collections.) The
existing toArray signatures are the best we could have done at the time
(and that was a very different time), but that doesn't mean we shouldn't
seek to do any better.
Here are what the client callsites might look like in various cases:
// status quo
Foo foos = ...toArray(new Foo); // ugh reflection
Foo foos = ...toArray(new Foo[xyz.size()]); // ugh ugly and racy
Foo foos = ...toArray(n -> new Foo[n]);
// David's alternative
Foo foos = ...toArray(Foo.class);
I don't see the "complexity of factories" being a big problem here -- if
people can deal with lambdas at all, this is a pretty simple case, and
its only a few characters longer than the "new Foo" version. I think
the lambda code reads pretty naturally. (Actually I find the "new
Foo" the most confusing -- why would I pass in a new empty array?)
More information about the lambda-libs-spec-observers