RFR(s): 8060192: Add default method Collection.toArray(generator)
Stuart Marks
stuart.marks at oracle.com
Tue Jun 19 00:53:17 UTC 2018
On 6/17/18 1:50 AM, Peter Levart wrote:
> It's a little strange that the generator function is used to construct an
> empty array (at least in the default method, but overrides will likely do the
> same if they want to avoid pre-zeroing overhead) in order to just extract the
> array's type from it. Someone could reasonably expect the provided function
> to be called with the exact length of needed array. The
> Collection.toArray(T[]) at least gives user an option to actually fully use
> the provided array, if user provides the correct length.
This is actually what we're trying to avoid. The toArray(T[]) API has to deal
with the cases where the array isn't the right length, and it reallocs or
inserts an extra null in cases where it isn't. To avoid this, people do
coll.toArray(new MyClass[coll.size()])
which turns out to be an anti-pattern. We could try to teach people to write
coll.toArray(new MyClass[0])
instead, and this works, but it's quite non-obvious. ("Why do I need to create a
zero-length array first?") (#include Tagir's comment about caching zero-length
instances) Instead we want to direct people to write
coll.toArray(MyClass[]::new)
which creates an array of the right type and requested length.
> The argument about using (and re-using) a method so that a method reference
> can be passed to the method without any unchecked casts is equally true for
> any of the 3 alternatives shown - the method itself might need unchecked
> casts, but its usage not:
>
> static List<String>[] array(int len) static Class<List<String>>
> elementType() static Class<List<String>[]> arrayType()
In principle all of these are possible. I don't see these as equal, though. It's
quite common to have to create arrays of a generic type, either inline with
unchecked casts, or possibly refactored into a method. I very rarely see Class
objects of generic types.
> But I can see that you want to align the API with Stream.toArray, while still
> providing the optimal implementation. It's just that the API doesn't fit the
> usecase. The function approach makes more sense in the Stream API where it
> is explicitly explained that it may be used to construct arrays needed to
> hold intermediary results of partitioned parallel execution too, but in
> Collection API it is usually the case to just provide a copy of the
> underlying data structure in the most optimal way (without pre-zeroing
> overhead) and for that purpose, 2nd and 3rd alternatives are a better fit.
Sure, the Stream API has additional uses for holding intermediate results. That
doesn't imply that Collection.toArray(generator) doesn't meet its use case
(which I described above).
I also don't see how class type tokens are a better fit. A type token is
"natural" if you're thinking of implementing it in terms of Arrays.copyOf() --
which it is right now, but that's an implementation detail.
> Suppose Stream took a different approach and used the 2nd or 3rd approach
> (which is universal). Would then Collection API also get the same method?
I'm not sure where this is headed. I'm pretty sure we considered using type
tokens for Stream.toArray() and rejected them in favor of toArray(generator). If
there had been some reason to use a type token instead, maybe we would have used
them, in which case we'd consider modifying Collection.toArray() would take a
type token as well. But I'm not aware of such a reason, so....
> It might have been the case in the past when Java generics were introduced,
> that class literals like List<String>.class would just confuse users,
> because most aspects of such type token would be erased and there were fears
> that enabling them might limit options for reified generics in the future.
> But long years have passed and java programmers have generally become
> acquainted with Java generics and erasure to the point where it doesn't
> confuse them any more, while reifying Java generics has been explored further
> in Valhalla to the point where it has become obvious that erasure of
> reference types is here to stay.
>
> Java could enable use of class literals like List<String>.class without fear
> that such literals would make users write buggy code or that such uses would
> limit options for Java in the future. Quite the contrary, they would enable
> users to write type-safer code than what they can write today. In the light
> of future Java (valhalla specialized generics), where such class literals
> make even more sense, enabling generic class literals could be viewed as a
> stepping stone that has some purpose in the short term too.
Again, I'm not sure where this is headed.
I certainly would not propose changing the language to allow generic class
literals in order to support the present use case (creating an array from a
collection).
As a longer term proposal, generic class literals might or might not be
worthwhile on their own merits. It's certainly an irritant not having them; I
could imagine some uses for them. But there are probably some downsides (such as
unsoundness) that will need to be thought through.
s'marks
More information about the core-libs-dev
mailing list